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

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 +9 -17
  26. package/lib/admin.js +375 -558
  27. package/lib/clearingHouse.d.ts +86 -109
  28. package/lib/clearingHouse.js +828 -895
  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 +17 -0
  42. package/lib/constants/numericConstants.js +23 -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 +118 -0
  61. package/lib/factory/bigNum.js +364 -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 +2287 -2938
  65. package/lib/index.d.ts +12 -4
  66. package/lib/index.js +12 -4
  67. package/lib/math/amm.d.ts +24 -29
  68. package/lib/math/amm.js +239 -178
  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 -7
  98. package/lib/orders.js +10 -80
  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 +110 -109
  107. package/lib/types.js +14 -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 +11 -4
  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 +218 -346
  128. package/src/clearingHouse.ts +983 -952
  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 +34 -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 +524 -0
  146. package/src/factory/oracleClient.ts +7 -4
  147. package/src/idl/clearing_house.json +2287 -2938
  148. package/src/index.ts +12 -4
  149. package/src/math/amm.ts +414 -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 +176 -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 +24 -126
  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 +111 -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
- );
204
- }
205
-
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];
215
- }
216
-
217
- public getFundingPaymentHistoryAccount(): FundingPaymentHistoryAccount {
218
- return this.accountSubscriber.getFundingPaymentHistoryAccount();
219
- }
220
-
221
- public getFundingRateHistoryAccount(): FundingRateHistoryAccount {
222
- return this.accountSubscriber.getFundingRateHistoryAccount();
223
- }
224
-
225
- public getTradeHistoryAccount(): TradeHistoryAccount {
226
- return this.accountSubscriber.getTradeHistoryAccount();
227
- }
228
-
229
- public getLiquidationHistoryAccount(): LiquidationHistoryAccount {
230
- return this.accountSubscriber.getLiquidationHistoryAccount();
228
+ return this.accountSubscriber.getStateAccountAndSlot().data;
231
229
  }
232
230
 
233
- public getDepositHistoryAccount(): DepositHistoryAccount {
234
- return this.accountSubscriber.getDepositHistoryAccount();
231
+ public getMarketAccount(marketIndex: BN | number): MarketAccount | undefined {
232
+ marketIndex = marketIndex instanceof BN ? marketIndex : new BN(marketIndex);
233
+ return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
235
234
  }
236
235
 
237
- public getCurveHistoryAccount(): ExtendedCurveHistoryAccount {
238
- return this.accountSubscriber.getCurveHistoryAccount();
236
+ public getMarketAccounts(): MarketAccount[] {
237
+ return this.accountSubscriber
238
+ .getMarketAccountsAndSlots()
239
+ .map((value) => value.data);
239
240
  }
240
241
 
241
- public getOrderHistoryAccount(): OrderHistoryAccount {
242
- return this.accountSubscriber.getOrderHistoryAccount();
242
+ public getBankAccount(bankIndex: BN | number): BankAccount | undefined {
243
+ bankIndex = bankIndex instanceof BN ? bankIndex : new BN(bankIndex);
244
+ return this.accountSubscriber.getBankAccountAndSlot(bankIndex).data;
243
245
  }
244
246
 
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;
247
+ public getQuoteAssetBankAccount(): BankAccount {
248
+ return this.accountSubscriber.getBankAccountAndSlot(QUOTE_ASSET_BANK_INDEX)
249
+ .data;
254
250
  }
255
251
 
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)
383
+ );
384
+ }
385
+
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
438
393
  );
439
- return this.userOrdersAccountPublicKey;
440
394
  }
441
395
 
442
- userOrdersExist?: boolean;
443
- async userOrdersAccountExists(): Promise<boolean> {
444
- if (this.userOrdersExist) {
445
- return this.userOrdersExist;
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
+ );
405
+ }
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
+ }
446
448
  }
447
- const userOrdersAccountRPCResponse =
448
- await this.connection.getParsedAccountInfo(
449
- await this.getUserOrdersAccountPublicKey()
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
+ }
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
+ }
499
+
500
+ return [
501
+ ...oracleAccountMap.values(),
502
+ ...bankAccountMap.values(),
503
+ ...marketAccountMap.values(),
504
+ ];
505
+ }
506
+
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
+ }
451
513
 
452
- this.userOrdersExist = userOrdersAccountRPCResponse.value !== null;
453
- return this.userOrdersExist;
514
+ public getOrderByUserId(userOrderId: number): Order | undefined {
515
+ return this.getUserAccount()?.orders.find(
516
+ (order) => order.userOrderId === userOrderId
517
+ );
454
518
  }
455
519
 
456
- public async depositCollateral(
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,352 @@ 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(
570
- amount: BN,
571
- collateralAccountPublicKey: PublicKey
572
- ): Promise<TransactionSignature> {
573
- return this.txSender.send(
574
- wrapInTx(
575
- await this.getWithdrawCollateralIx(amount, collateralAccountPublicKey)
576
- ),
577
- [],
578
- this.opts
579
- );
580
- }
581
-
582
- public async getWithdrawCollateralIx(
583
- amount: BN,
584
- collateralAccountPublicKey: PublicKey
585
- ): Promise<TransactionInstruction> {
586
- const userAccountPublicKey = await this.getUserAccountPublicKey();
587
- const user: any = await this.program.account.user.fetch(
588
- userAccountPublicKey
589
- );
590
-
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
- },
608
- });
609
- }
610
-
611
- public async openPosition(
612
- direction: PositionDirection,
670
+ public async withdraw(
613
671
  amount: BN,
614
- marketIndex: BN,
615
- limitPrice?: BN,
616
- discountToken?: PublicKey,
617
- referrer?: PublicKey
672
+ bankIndex: BN,
673
+ userTokenAccount: PublicKey,
674
+ reduceOnly = false
618
675
  ): Promise<TransactionSignature> {
619
- return await this.txSender.send(
676
+ const { txSig } = await this.txSender.send(
620
677
  wrapInTx(
621
- await this.getOpenPositionIx(
622
- direction,
678
+ await this.getWithdrawIx(
623
679
  amount,
624
- marketIndex,
625
- limitPrice,
626
- discountToken,
627
- referrer
680
+ bankIndex,
681
+ userTokenAccount,
682
+ reduceOnly
628
683
  )
629
684
  ),
630
685
  [],
631
686
  this.opts
632
687
  );
688
+ return txSig;
633
689
  }
634
690
 
635
- public async getOpenPositionIx(
636
- direction: PositionDirection,
691
+ public async getWithdrawIx(
637
692
  amount: BN,
638
- marketIndex: BN,
639
- limitPrice?: BN,
640
- discountToken?: PublicKey,
641
- referrer?: PublicKey
693
+ bankIndex: BN,
694
+ userTokenAccount: PublicKey,
695
+ reduceOnly = false
642
696
  ): Promise<TransactionInstruction> {
643
697
  const userAccountPublicKey = await this.getUserAccountPublicKey();
644
- const userAccount = await this.getUserAccount();
645
-
646
- if (limitPrice == undefined) {
647
- limitPrice = new BN(0); // no limit
648
- }
649
698
 
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
- }
699
+ const remainingAccounts = this.getRemainingAccounts({
700
+ writableBankIndex: bankIndex,
701
+ });
671
702
 
672
- const priceOracle =
673
- this.getMarketsAccount().markets[marketIndex.toNumber()].amm.oracle;
703
+ const bank = this.getBankAccount(bankIndex);
674
704
 
675
- const state = this.getStateAccount();
676
- return await this.program.instruction.openPosition(
677
- direction,
705
+ return await this.program.instruction.withdraw(
706
+ bankIndex,
678
707
  amount,
679
- marketIndex,
680
- limitPrice,
681
- optionalAccounts,
708
+ reduceOnly,
682
709
  {
683
710
  accounts: {
684
711
  state: await this.getStatePublicKey(),
712
+ bank: bank.pubkey,
713
+ bankVault: bank.vault,
714
+ bankVaultAuthority: bank.vaultAuthority,
685
715
  user: userAccountPublicKey,
716
+ userTokenAccount: userTokenAccount,
686
717
  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,
718
+ tokenProgram: TOKEN_PROGRAM_ID,
693
719
  },
694
- remainingAccounts: remainingAccounts,
720
+ remainingAccounts,
695
721
  }
696
722
  );
697
723
  }
698
724
 
699
- public async initializeUserOrdersThenPlaceOrder(
700
- orderParams: OrderParams,
701
- discountToken?: PublicKey,
702
- referrer?: PublicKey
703
- ): 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)
711
- );
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
- }
719
-
720
- public async placeOrder(
721
- orderParams: OrderParams,
722
- discountToken?: PublicKey,
723
- referrer?: PublicKey
725
+ public async transferDeposit(
726
+ amount: BN,
727
+ bankIndex: BN,
728
+ fromUserId: number,
729
+ toUserId: number
724
730
  ): Promise<TransactionSignature> {
725
- return await this.txSender.send(
731
+ const { txSig } = await this.txSender.send(
726
732
  wrapInTx(
727
- await this.getPlaceOrderIx(orderParams, discountToken, referrer)
733
+ await this.getTransferDepositIx(amount, bankIndex, fromUserId, toUserId)
728
734
  ),
729
735
  [],
730
736
  this.opts
731
737
  );
738
+ return txSig;
732
739
  }
733
740
 
734
- public async getPlaceOrderIx(
735
- orderParams: OrderParams,
736
- discountToken?: PublicKey,
737
- referrer?: PublicKey
741
+ public async getTransferDepositIx(
742
+ amount: BN,
743
+ bankIndex: BN,
744
+ fromUserId: number,
745
+ toUserId: number
738
746
  ): Promise<TransactionInstruction> {
739
- const userAccountPublicKey = await this.getUserAccountPublicKey();
740
- const userAccount = await this.getUserAccount();
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
+ );
741
757
 
742
- const priceOracle =
743
- this.getMarketsAccount().markets[orderParams.marketIndex.toNumber()].amm
744
- .oracle;
758
+ const remainingAccounts = this.getRemainingAccounts({
759
+ writableBankIndex: bankIndex,
760
+ });
745
761
 
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
- }
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
+ }
753
772
 
754
- remainingAccounts.push({
755
- pubkey: discountToken,
756
- isWritable: false,
757
- isSigner: false,
758
- });
759
- }
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
780
+ );
781
+ return txSig;
782
+ }
760
783
 
761
- if (orderParams.optionalAccounts.referrer) {
762
- if (!referrer) {
763
- throw Error(
764
- 'Optional accounts specified referrer but no referrer present'
765
- );
766
- }
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
+ }
767
794
 
768
- remainingAccounts.push({
769
- pubkey: referrer,
770
- isWritable: false,
771
- isSigner: false,
772
- });
773
- }
795
+ public async openPosition(
796
+ direction: PositionDirection,
797
+ amount: BN,
798
+ marketIndex: BN,
799
+ limitPrice?: BN
800
+ ): Promise<TransactionSignature> {
801
+ return await this.placeAndTake(
802
+ getMarketOrderParams(
803
+ marketIndex,
804
+ direction,
805
+ ZERO,
806
+ amount,
807
+ false,
808
+ limitPrice
809
+ )
810
+ );
811
+ }
774
812
 
775
- if (!orderParams.oraclePriceOffset.eq(ZERO)) {
776
- remainingAccounts.push({
777
- pubkey: priceOracle,
778
- isWritable: false,
779
- isSigner: false,
780
- });
781
- }
813
+ public async placeOrder(
814
+ orderParams: OrderParams
815
+ ): Promise<TransactionSignature> {
816
+ const { txSig, slot } = await this.txSender.send(
817
+ wrapInTx(await this.getPlaceOrderIx(orderParams)),
818
+ [],
819
+ this.opts
820
+ );
821
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
822
+ return txSig;
823
+ }
824
+
825
+ public async getPlaceOrderIx(
826
+ orderParams: OrderParams
827
+ ): Promise<TransactionInstruction> {
828
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
829
+
830
+ const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
831
+ .oracle;
832
+
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)),
848
877
  [],
849
878
  this.opts
850
879
  );
880
+ return txSig;
851
881
  }
852
882
 
853
- public async getCancelOrderIx(
854
- orderId: BN,
855
- oracle?: PublicKey
883
+ public async getUpdateAMMsIx(
884
+ marketIndexes: BN[]
856
885
  ): Promise<TransactionInstruction> {
857
- const userAccountPublicKey = await this.getUserAccountPublicKey();
858
- const userAccount = await this.getUserAccount();
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);
859
907
 
860
- const state = this.getStateAccount();
861
- const orderState = this.getOrderStateAccount();
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
+ }
862
916
 
863
- const remainingAccounts = [];
864
- if (oracle) {
865
- remainingAccounts.push({
866
- pubkey: oracle,
867
- isWritable: false,
868
- isSigner: false,
869
- });
870
- }
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> {
927
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
871
928
 
872
- return await this.program.instruction.cancelOrder(orderId, {
929
+ const remainingAccounts = this.getRemainingAccounts({});
930
+
931
+ return await this.program.instruction.cancelOrder(orderId ?? null, {
873
932
  accounts: {
874
933
  state: await this.getStatePublicKey(),
875
934
  user: userAccountPublicKey,
876
935
  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,
884
936
  },
885
937
  remainingAccounts,
886
938
  });
887
939
  }
888
940
 
889
941
  public async cancelOrderByUserId(
890
- userOrderId: number,
891
- oracle?: PublicKey
942
+ userOrderId: number
892
943
  ): Promise<TransactionSignature> {
893
- return await this.txSender.send(
894
- wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId, oracle)),
944
+ const { txSig } = await this.txSender.send(
945
+ wrapInTx(await this.getCancelOrderByUserIdIx(userOrderId)),
895
946
  [],
896
947
  this.opts
897
948
  );
949
+ return txSig;
898
950
  }
899
951
 
900
952
  public async getCancelOrderByUserIdIx(
901
- userOrderId: number,
902
- oracle?: PublicKey
953
+ userOrderId: number
903
954
  ): Promise<TransactionInstruction> {
904
955
  const userAccountPublicKey = await this.getUserAccountPublicKey();
905
- const userAccount = await this.getUserAccount();
906
956
 
907
- const state = this.getStateAccount();
908
- const orderState = this.getOrderStateAccount();
957
+ const order = this.getOrderByUserId(userOrderId);
958
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
909
959
 
910
- const remainingAccounts = [];
911
- if (oracle) {
912
- remainingAccounts.push({
913
- pubkey: oracle,
914
- isWritable: false,
915
- isSigner: false,
916
- });
917
- }
960
+ const remainingAccounts = this.getRemainingAccounts({});
918
961
 
919
962
  return await this.program.instruction.cancelOrderByUserId(userOrderId, {
920
963
  accounts: {
921
964
  state: await this.getStatePublicKey(),
922
965
  user: userAccountPublicKey,
923
966
  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,
967
+ oracle,
931
968
  },
932
969
  remainingAccounts,
933
970
  });
934
971
  }
935
972
 
936
973
  public async cancelAllOrders(
937
- oracles?: PublicKey[],
938
974
  bestEffort?: boolean
939
975
  ): Promise<TransactionSignature> {
940
- return await this.txSender.send(
941
- wrapInTx(await this.getCancelAllOrdersIx(oracles, bestEffort)),
976
+ const { txSig } = await this.txSender.send(
977
+ wrapInTx(await this.getCancelAllOrdersIx(bestEffort)),
942
978
  [],
943
979
  this.opts
944
980
  );
981
+ return txSig;
945
982
  }
946
983
 
947
984
  public async getCancelAllOrdersIx(
948
- oracles: PublicKey[],
949
985
  bestEffort?: boolean
950
986
  ): Promise<TransactionInstruction> {
951
987
  const userAccountPublicKey = await this.getUserAccountPublicKey();
952
- const userAccount = await this.getUserAccount();
953
988
 
954
- const state = this.getStateAccount();
955
- const orderState = this.getOrderStateAccount();
989
+ const remainingAccounts = this.getRemainingAccounts({});
956
990
 
957
- const remainingAccounts = [];
958
- for (const oracle of oracles) {
991
+ for (const order of this.getUserAccount().orders) {
992
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
959
993
  remainingAccounts.push({
960
994
  pubkey: oracle,
961
995
  isWritable: false,
@@ -968,28 +1002,19 @@ export class ClearingHouse {
968
1002
  state: await this.getStatePublicKey(),
969
1003
  user: userAccountPublicKey,
970
1004
  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
1005
  },
979
1006
  remainingAccounts,
980
1007
  });
981
1008
  }
982
1009
 
983
1010
  public async cancelOrdersByMarketAndSide(
984
- oracles?: PublicKey[],
985
1011
  bestEffort?: boolean,
986
1012
  marketIndexOnly?: BN,
987
1013
  directionOnly?: PositionDirection
988
1014
  ): Promise<TransactionSignature> {
989
- return await this.txSender.send(
1015
+ const { txSig } = await this.txSender.send(
990
1016
  wrapInTx(
991
1017
  await this.getCancelOrdersByMarketAndSideIx(
992
- oracles,
993
1018
  bestEffort,
994
1019
  marketIndexOnly,
995
1020
  directionOnly
@@ -998,22 +1023,20 @@ export class ClearingHouse {
998
1023
  [],
999
1024
  this.opts
1000
1025
  );
1026
+ return txSig;
1001
1027
  }
1002
1028
 
1003
1029
  public async getCancelOrdersByMarketAndSideIx(
1004
- oracles: PublicKey[],
1005
1030
  bestEffort?: boolean,
1006
1031
  marketIndexOnly?: BN,
1007
1032
  directionOnly?: PositionDirection
1008
1033
  ): Promise<TransactionInstruction> {
1009
1034
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1010
- const userAccount = await this.getUserAccount();
1011
1035
 
1012
- const state = this.getStateAccount();
1013
- const orderState = this.getOrderStateAccount();
1036
+ const remainingAccounts = this.getRemainingAccounts({});
1014
1037
 
1015
- const remainingAccounts = [];
1016
- for (const oracle of oracles) {
1038
+ for (const order of this.getUserAccount().orders) {
1039
+ const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
1017
1040
  remainingAccounts.push({
1018
1041
  pubkey: oracle,
1019
1042
  isWritable: false,
@@ -1030,13 +1053,6 @@ export class ClearingHouse {
1030
1053
  state: await this.getStatePublicKey(),
1031
1054
  user: userAccountPublicKey,
1032
1055
  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
1056
  },
1041
1057
  remainingAccounts,
1042
1058
  }
@@ -1045,320 +1061,432 @@ export class ClearingHouse {
1045
1061
 
1046
1062
  public async fillOrder(
1047
1063
  userAccountPublicKey: PublicKey,
1048
- userOrdersAccountPublicKey: PublicKey,
1049
- order: Order
1064
+ user: UserAccount,
1065
+ order?: Order,
1066
+ makerInfo?: MakerInfo
1050
1067
  ): Promise<TransactionSignature> {
1051
- return await this.txSender.send(
1068
+ const { txSig } = await this.txSender.send(
1052
1069
  wrapInTx(
1053
- await this.getFillOrderIx(
1054
- userAccountPublicKey,
1055
- userOrdersAccountPublicKey,
1056
- order
1057
- )
1070
+ await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
1058
1071
  ),
1059
1072
  [],
1060
1073
  this.opts
1061
1074
  );
1075
+ return txSig;
1062
1076
  }
1063
1077
 
1064
1078
  public async getFillOrderIx(
1065
1079
  userAccountPublicKey: PublicKey,
1066
- userOrdersAccountPublicKey: PublicKey,
1067
- order: Order
1080
+ userAccount: UserAccount,
1081
+ order: Order,
1082
+ makerInfo?: MakerInfo
1068
1083
  ): Promise<TransactionInstruction> {
1069
1084
  const fillerPublicKey = await this.getUserAccountPublicKey();
1070
- const userAccount: any = await this.program.account.user.fetch(
1071
- userAccountPublicKey
1072
- );
1073
1085
 
1074
1086
  const marketIndex = order.marketIndex;
1075
- const oracle = this.getMarket(marketIndex).amm.oracle;
1087
+ const marketAccount = this.getMarketAccount(marketIndex);
1088
+ const oracle = marketAccount.amm.oracle;
1076
1089
 
1077
- const state = this.getStateAccount();
1078
- const orderState = this.getOrderStateAccount();
1090
+ const bankAccountInfos = [
1091
+ {
1092
+ pubkey: this.getQuoteAssetBankAccount().pubkey,
1093
+ isSigner: false,
1094
+ isWritable: true,
1095
+ },
1096
+ ];
1097
+ const marketAccountInfos = [
1098
+ {
1099
+ pubkey: marketAccount.pubkey,
1100
+ isWritable: true,
1101
+ isSigner: false,
1102
+ },
1103
+ ];
1104
+ const oracleAccountInfos = [
1105
+ {
1106
+ pubkey: marketAccount.amm.oracle,
1107
+ isWritable: false,
1108
+ isSigner: false,
1109
+ },
1110
+ ];
1111
+ for (const position of userAccount.positions) {
1112
+ if (
1113
+ !positionIsAvailable(position) &&
1114
+ !position.marketIndex.eq(order.marketIndex)
1115
+ ) {
1116
+ const market = this.getMarketAccount(position.marketIndex);
1117
+ marketAccountInfos.push({
1118
+ pubkey: market.pubkey,
1119
+ isWritable: false,
1120
+ isSigner: false,
1121
+ });
1122
+ oracleAccountInfos.push({
1123
+ pubkey: market.amm.oracle,
1124
+ isWritable: false,
1125
+ isSigner: false,
1126
+ });
1127
+ }
1128
+ }
1129
+ const remainingAccounts = oracleAccountInfos.concat(
1130
+ bankAccountInfos.concat(marketAccountInfos)
1131
+ );
1079
1132
 
1080
- const remainingAccounts = [];
1081
- if (!order.referrer.equals(PublicKey.default)) {
1133
+ if (makerInfo) {
1082
1134
  remainingAccounts.push({
1083
- pubkey: order.referrer,
1135
+ pubkey: makerInfo.maker,
1084
1136
  isWritable: true,
1085
1137
  isSigner: false,
1086
1138
  });
1087
1139
  }
1088
1140
 
1089
1141
  const orderId = order.orderId;
1090
- return await this.program.instruction.fillOrder(orderId, {
1142
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1143
+ return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1091
1144
  accounts: {
1092
1145
  state: await this.getStatePublicKey(),
1093
1146
  filler: fillerPublicKey,
1094
1147
  user: userAccountPublicKey,
1095
1148
  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
1149
  oracle: oracle,
1106
1150
  },
1107
1151
  remainingAccounts,
1108
1152
  });
1109
1153
  }
1110
1154
 
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(
1133
- orderParams: OrderParams,
1134
- discountToken?: PublicKey,
1135
- referrer?: PublicKey
1155
+ public async triggerOrder(
1156
+ userAccountPublicKey: PublicKey,
1157
+ user: UserAccount,
1158
+ order: Order
1136
1159
  ): Promise<TransactionSignature> {
1137
- return await this.txSender.send(
1138
- wrapInTx(
1139
- await this.getPlaceAndFillOrderIx(orderParams, discountToken, referrer)
1140
- ),
1160
+ const { txSig } = await this.txSender.send(
1161
+ wrapInTx(await this.getTriggerOrderIx(userAccountPublicKey, user, order)),
1141
1162
  [],
1142
1163
  this.opts
1143
1164
  );
1165
+ return txSig;
1144
1166
  }
1145
1167
 
1146
- public async getPlaceAndFillOrderIx(
1147
- orderParams: OrderParams,
1148
- discountToken?: PublicKey,
1149
- referrer?: PublicKey
1168
+ public async getTriggerOrderIx(
1169
+ userAccountPublicKey: PublicKey,
1170
+ userAccount: UserAccount,
1171
+ order: Order
1150
1172
  ): Promise<TransactionInstruction> {
1151
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1152
- const userAccount = await this.getUserAccount();
1153
-
1154
- const priceOracle =
1155
- this.getMarketsAccount().markets[orderParams.marketIndex.toNumber()].amm
1156
- .oracle;
1173
+ const fillerPublicKey = await this.getUserAccountPublicKey();
1157
1174
 
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
- }
1175
+ const marketIndex = order.marketIndex;
1176
+ const marketAccount = this.getMarketAccount(marketIndex);
1165
1177
 
1166
- remainingAccounts.push({
1167
- pubkey: discountToken,
1168
- isWritable: false,
1178
+ const bankAccountInfos = [
1179
+ {
1180
+ pubkey: this.getQuoteAssetBankAccount().pubkey,
1169
1181
  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
+ isWritable: true,
1183
+ },
1184
+ ];
1185
+ const marketAccountInfos = [
1186
+ {
1187
+ pubkey: marketAccount.pubkey,
1182
1188
  isWritable: true,
1183
1189
  isSigner: false,
1184
- });
1190
+ },
1191
+ ];
1192
+ const oracleAccountInfos = [
1193
+ {
1194
+ pubkey: marketAccount.amm.oracle,
1195
+ isWritable: false,
1196
+ isSigner: false,
1197
+ },
1198
+ ];
1199
+ for (const position of userAccount.positions) {
1200
+ if (
1201
+ !positionIsAvailable(position) &&
1202
+ !position.marketIndex.eq(order.marketIndex)
1203
+ ) {
1204
+ const market = this.getMarketAccount(position.marketIndex);
1205
+ marketAccountInfos.push({
1206
+ pubkey: market.pubkey,
1207
+ isWritable: false,
1208
+ isSigner: false,
1209
+ });
1210
+ oracleAccountInfos.push({
1211
+ pubkey: market.amm.oracle,
1212
+ isWritable: false,
1213
+ isSigner: false,
1214
+ });
1215
+ }
1185
1216
  }
1217
+ const remainingAccounts = oracleAccountInfos.concat(
1218
+ bankAccountInfos.concat(marketAccountInfos)
1219
+ );
1186
1220
 
1187
- const state = this.getStateAccount();
1188
- const orderState = this.getOrderStateAccount();
1189
- return await this.program.instruction.placeAndFillOrder(orderParams, {
1221
+ const orderId = order.orderId;
1222
+ return await this.program.instruction.triggerOrder(orderId, {
1190
1223
  accounts: {
1191
1224
  state: await this.getStatePublicKey(),
1225
+ filler: fillerPublicKey,
1192
1226
  user: userAccountPublicKey,
1193
1227
  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
1228
  },
1205
1229
  remainingAccounts,
1206
1230
  });
1207
1231
  }
1208
1232
 
1209
- /**
1210
- * 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
- * @param marketIndex
1212
- * @param discountToken
1213
- * @param referrer
1214
- * @returns
1215
- */
1216
- public async closePosition(
1217
- marketIndex: BN,
1218
- discountToken?: PublicKey,
1219
- referrer?: PublicKey
1233
+ public async placeAndTake(
1234
+ orderParams: OrderParams,
1235
+ makerInfo?: MakerInfo
1220
1236
  ): Promise<TransactionSignature> {
1221
- return await this.txSender.send(
1222
- wrapInTx(
1223
- await this.getClosePositionIx(marketIndex, discountToken, referrer)
1224
- ),
1237
+ const { txSig, slot } = await this.txSender.send(
1238
+ wrapInTx(await this.getPlaceAndTakeIx(orderParams, makerInfo)),
1225
1239
  [],
1226
1240
  this.opts
1227
1241
  );
1242
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
1243
+ return txSig;
1228
1244
  }
1229
1245
 
1230
- public async getClosePositionIx(
1231
- marketIndex: BN,
1232
- discountToken?: PublicKey,
1233
- referrer?: PublicKey
1246
+ public async getPlaceAndTakeIx(
1247
+ orderParams: OrderParams,
1248
+ makerInfo?: MakerInfo
1234
1249
  ): Promise<TransactionInstruction> {
1235
1250
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1236
- const userAccount = await this.getUserAccount();
1237
1251
 
1238
- const priceOracle =
1239
- this.getMarketsAccount().markets[marketIndex.toNumber()].amm.oracle;
1252
+ const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
1253
+ .oracle;
1240
1254
 
1241
- const optionalAccounts = {
1242
- discountToken: false,
1243
- referrer: false,
1244
- };
1245
- const remainingAccounts = [];
1246
- if (discountToken) {
1247
- optionalAccounts.discountToken = true;
1255
+ const remainingAccounts = this.getRemainingAccounts({
1256
+ writableMarketIndex: orderParams.marketIndex,
1257
+ writableBankIndex: QUOTE_ASSET_BANK_INDEX,
1258
+ });
1259
+
1260
+ let makerOrderId = null;
1261
+ if (makerInfo) {
1262
+ makerOrderId = makerInfo.order.orderId;
1248
1263
  remainingAccounts.push({
1249
- pubkey: discountToken,
1250
- isWritable: false,
1264
+ pubkey: makerInfo.maker,
1251
1265
  isSigner: false,
1252
- });
1253
- }
1254
- if (referrer) {
1255
- optionalAccounts.referrer = true;
1256
- remainingAccounts.push({
1257
- pubkey: referrer,
1258
1266
  isWritable: true,
1259
- isSigner: false,
1260
1267
  });
1261
1268
  }
1262
1269
 
1263
- const state = this.getStateAccount();
1264
- return await this.program.instruction.closePosition(
1265
- marketIndex,
1266
- optionalAccounts,
1270
+ return await this.program.instruction.placeAndTake(
1271
+ orderParams,
1272
+ makerOrderId,
1267
1273
  {
1268
1274
  accounts: {
1269
1275
  state: await this.getStatePublicKey(),
1270
1276
  user: userAccountPublicKey,
1271
1277
  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
1278
  oracle: priceOracle,
1278
1279
  },
1279
- remainingAccounts: remainingAccounts,
1280
+ remainingAccounts,
1280
1281
  }
1281
1282
  );
1282
1283
  }
1283
1284
 
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
- }
1285
+ /**
1286
+ * Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
1287
+ * @param marketIndex
1288
+ * @returns
1289
+ */
1290
+ public async closePosition(marketIndex: BN): Promise<TransactionSignature> {
1291
+ const userPosition = this.getUser().getUserPosition(marketIndex);
1292
+ if (!userPosition) {
1293
+ throw Error(`No position in market ${marketIndex.toString()}`);
1294
+ }
1295
+
1296
+ return await this.placeAndTake(
1297
+ getMarketOrderParams(
1298
+ marketIndex,
1299
+ findDirectionToClose(userPosition),
1300
+ ZERO,
1301
+ userPosition.baseAssetAmount,
1302
+ true,
1303
+ undefined
1304
+ )
1305
+ );
1306
+ }
1294
1307
 
1308
+ public async settlePNLs(
1309
+ users: {
1310
+ settleeUserAccountPublicKey: PublicKey;
1311
+ settleeUserAccount: UserAccount;
1312
+ }[],
1313
+ marketIndex: BN
1314
+ ): Promise<TransactionSignature> {
1315
+ const ixs = [];
1316
+ for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
1295
1317
  ixs.push(
1296
- await this.getClosePositionIx(
1297
- userPosition.marketIndex,
1298
- discountToken,
1299
- referrer
1318
+ await this.settlePNLIx(
1319
+ settleeUserAccountPublicKey,
1320
+ settleeUserAccount,
1321
+ marketIndex
1300
1322
  )
1301
1323
  );
1302
1324
  }
1303
1325
 
1304
1326
  const tx = new Transaction().add(...ixs);
1305
1327
 
1306
- return this.txSender.send(tx, [], this.opts);
1328
+ const { txSig } = await this.txSender.send(tx, [], this.opts);
1329
+ return txSig;
1330
+ }
1331
+
1332
+ public async settlePNL(
1333
+ settleeUserAccountPublicKey: PublicKey,
1334
+ settleeUserAccount: UserAccount,
1335
+ marketIndex: BN
1336
+ ): Promise<TransactionSignature> {
1337
+ const { txSig } = await this.txSender.send(
1338
+ wrapInTx(
1339
+ await this.settlePNLIx(
1340
+ settleeUserAccountPublicKey,
1341
+ settleeUserAccount,
1342
+ marketIndex
1343
+ )
1344
+ ),
1345
+ [],
1346
+ this.opts
1347
+ );
1348
+ return txSig;
1349
+ }
1350
+
1351
+ public async settlePNLIx(
1352
+ settleeUserAccountPublicKey: PublicKey,
1353
+ settleeUserAccount: UserAccount,
1354
+ marketIndex: BN
1355
+ ): Promise<TransactionInstruction> {
1356
+ const marketAccountMap = new Map<number, AccountMeta>();
1357
+ const oracleAccountMap = new Map<string, AccountMeta>();
1358
+ const bankAccountMap = new Map<number, AccountMeta>();
1359
+ for (const position of settleeUserAccount.positions) {
1360
+ if (!positionIsAvailable(position)) {
1361
+ const market = this.getMarketAccount(position.marketIndex);
1362
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1363
+ pubkey: market.pubkey,
1364
+ isWritable: true, // TODO
1365
+ isSigner: false,
1366
+ });
1367
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1368
+ pubkey: market.amm.oracle,
1369
+ isWritable: false,
1370
+ isSigner: false,
1371
+ });
1372
+ }
1373
+ }
1374
+
1375
+ for (const userBankBalance of settleeUserAccount.bankBalances) {
1376
+ if (!userBankBalance.balance.eq(QUOTE_ASSET_BANK_INDEX)) {
1377
+ const bankAccount = this.getBankAccount(userBankBalance.bankIndex);
1378
+ bankAccountMap.set(userBankBalance.bankIndex.toNumber(), {
1379
+ pubkey: bankAccount.pubkey,
1380
+ isSigner: false,
1381
+ isWritable: false,
1382
+ });
1383
+ if (!bankAccount.bankIndex.eq(ZERO)) {
1384
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1385
+ pubkey: bankAccount.oracle,
1386
+ isSigner: false,
1387
+ isWritable: false,
1388
+ });
1389
+ }
1390
+ }
1391
+ }
1392
+
1393
+ const marketAccount = this.getMarketAccount(marketIndex.toNumber());
1394
+ marketAccountMap.set(marketIndex.toNumber(), {
1395
+ pubkey: marketAccount.pubkey,
1396
+ isSigner: false,
1397
+ isWritable: true,
1398
+ });
1399
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1400
+ pubkey: marketAccount.amm.oracle,
1401
+ isSigner: false,
1402
+ isWritable: false,
1403
+ });
1404
+
1405
+ bankAccountMap.set(QUOTE_ASSET_BANK_INDEX.toNumber(), {
1406
+ pubkey: this.getBankAccount(QUOTE_ASSET_BANK_INDEX).pubkey,
1407
+ isSigner: false,
1408
+ isWritable: true,
1409
+ });
1410
+
1411
+ const remainingAccounts = [
1412
+ ...oracleAccountMap.values(),
1413
+ ...bankAccountMap.values(),
1414
+ ...marketAccountMap.values(),
1415
+ ];
1416
+
1417
+ return await this.program.instruction.settlePnl(marketIndex, {
1418
+ accounts: {
1419
+ state: await this.getStatePublicKey(),
1420
+ authority: this.wallet.publicKey,
1421
+ user: settleeUserAccountPublicKey,
1422
+ },
1423
+ remainingAccounts: remainingAccounts,
1424
+ });
1307
1425
  }
1308
1426
 
1309
1427
  public async liquidate(
1310
1428
  liquidateeUserAccountPublicKey: PublicKey
1311
1429
  ): Promise<TransactionSignature> {
1312
- return this.txSender.send(
1430
+ const { txSig } = await this.txSender.send(
1313
1431
  wrapInTx(await this.getLiquidateIx(liquidateeUserAccountPublicKey)),
1314
1432
  [],
1315
1433
  this.opts
1316
1434
  );
1435
+ return txSig;
1317
1436
  }
1318
1437
 
1319
1438
  public async getLiquidateIx(
1320
1439
  liquidateeUserAccountPublicKey: PublicKey
1321
1440
  ): Promise<TransactionInstruction> {
1322
1441
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1323
-
1324
- const liquidateeUserAccount: any = await this.program.account.user.fetch(
1442
+ const liquidateeUserAccount = (await this.program.account.user.fetch(
1325
1443
  liquidateeUserAccountPublicKey
1326
- );
1327
- const liquidateePositions: any =
1328
- await this.program.account.userPositions.fetch(
1329
- liquidateeUserAccount.positions
1330
- );
1331
- const markets = this.getMarketsAccount();
1444
+ )) as UserAccount;
1332
1445
 
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({
1446
+ const bankAccountInfos = [
1447
+ {
1448
+ pubkey: this.getQuoteAssetBankAccount().pubkey,
1449
+ isSigner: false,
1450
+ isWritable: true,
1451
+ },
1452
+ ];
1453
+ const marketAccountInfos = [];
1454
+ const oracleAccountInfos = [];
1455
+ for (const position of liquidateeUserAccount.positions) {
1456
+ if (!positionIsAvailable(position)) {
1457
+ const market = this.getMarketAccount(position.marketIndex);
1458
+ const marketPublicKey = await getMarketPublicKey(
1459
+ this.program.programId,
1460
+ position.marketIndex
1461
+ );
1462
+ marketAccountInfos.push({
1463
+ pubkey: marketPublicKey,
1464
+ isWritable: true,
1465
+ isSigner: false,
1466
+ });
1467
+ oracleAccountInfos.push({
1338
1468
  pubkey: market.amm.oracle,
1339
1469
  isWritable: false,
1340
1470
  isSigner: false,
1341
1471
  });
1342
1472
  }
1343
1473
  }
1474
+ const remainingAccounts = oracleAccountInfos.concat(
1475
+ bankAccountInfos.concat(marketAccountInfos)
1476
+ );
1344
1477
 
1345
1478
  const state = this.getStateAccount();
1479
+ const quoteAssetBankAccount = this.getQuoteAssetBankAccount();
1346
1480
  return await this.program.instruction.liquidate({
1347
1481
  accounts: {
1348
1482
  state: await this.getStatePublicKey(),
1349
1483
  authority: this.wallet.publicKey,
1350
1484
  user: liquidateeUserAccountPublicKey,
1351
1485
  liquidator: userAccountPublicKey,
1352
- collateralVault: state.collateralVault,
1353
- collateralVaultAuthority: state.collateralVaultAuthority,
1486
+ bankVault: quoteAssetBankAccount.vault,
1487
+ bankVaultAuthority: quoteAssetBankAccount.vaultAuthority,
1354
1488
  insuranceVault: state.insuranceVault,
1355
- insuranceVaultAuthority: state.insuranceVaultAuthority,
1356
1489
  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
1490
  },
1363
1491
  remainingAccounts: remainingAccounts,
1364
1492
  });
@@ -1368,54 +1496,68 @@ export class ClearingHouse {
1368
1496
  oracle: PublicKey,
1369
1497
  marketIndex: BN
1370
1498
  ): Promise<TransactionSignature> {
1371
- return this.txSender.send(
1499
+ const { txSig } = await this.txSender.send(
1372
1500
  wrapInTx(await this.getUpdateFundingRateIx(oracle, marketIndex)),
1373
1501
  [],
1374
1502
  this.opts
1375
1503
  );
1504
+ return txSig;
1376
1505
  }
1377
1506
 
1378
1507
  public async getUpdateFundingRateIx(
1379
1508
  oracle: PublicKey,
1380
1509
  marketIndex: BN
1381
1510
  ): Promise<TransactionInstruction> {
1382
- const state = this.getStateAccount();
1383
1511
  return await this.program.instruction.updateFundingRate(marketIndex, {
1384
1512
  accounts: {
1385
1513
  state: await this.getStatePublicKey(),
1386
- markets: state.markets,
1514
+ market: await getMarketPublicKey(this.program.programId, marketIndex),
1387
1515
  oracle: oracle,
1388
- fundingRateHistory: state.fundingRateHistory,
1389
1516
  },
1390
1517
  });
1391
1518
  }
1392
1519
 
1393
1520
  public async settleFundingPayment(
1394
- userAccount: PublicKey,
1395
- userPositionsAccount: PublicKey
1521
+ userAccount: PublicKey
1396
1522
  ): Promise<TransactionSignature> {
1397
- return this.txSender.send(
1398
- wrapInTx(
1399
- await this.getSettleFundingPaymentIx(userAccount, userPositionsAccount)
1400
- ),
1523
+ const { txSig } = await this.txSender.send(
1524
+ wrapInTx(await this.getSettleFundingPaymentIx(userAccount)),
1401
1525
  [],
1402
1526
  this.opts
1403
1527
  );
1528
+ return txSig;
1404
1529
  }
1405
1530
 
1406
1531
  public async getSettleFundingPaymentIx(
1407
- userAccount: PublicKey,
1408
- userPositionsAccount: PublicKey
1532
+ userAccount: PublicKey
1409
1533
  ): Promise<TransactionInstruction> {
1410
- const state = this.getStateAccount();
1534
+ const user = (await this.program.account.user.fetch(
1535
+ userAccount
1536
+ )) as UserAccount;
1537
+
1538
+ const userPositions = user.positions;
1539
+
1540
+ const remainingAccounts = [];
1541
+ for (const position of userPositions) {
1542
+ if (!positionIsAvailable(position)) {
1543
+ const marketPublicKey = await getMarketPublicKey(
1544
+ this.program.programId,
1545
+ position.marketIndex
1546
+ );
1547
+ remainingAccounts.push({
1548
+ pubkey: marketPublicKey,
1549
+ isWritable: false,
1550
+ isSigner: false,
1551
+ });
1552
+ }
1553
+ }
1554
+
1411
1555
  return await this.program.instruction.settleFundingPayment({
1412
1556
  accounts: {
1413
1557
  state: await this.getStatePublicKey(),
1414
- markets: state.markets,
1415
1558
  user: userAccount,
1416
- userPositions: userPositionsAccount,
1417
- fundingPaymentHistory: state.fundingPaymentHistory,
1418
1559
  },
1560
+ remainingAccounts,
1419
1561
  });
1420
1562
  }
1421
1563
 
@@ -1423,121 +1565,10 @@ export class ClearingHouse {
1423
1565
  this.eventEmitter.emit(eventName, data);
1424
1566
  }
1425
1567
 
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
- }
1568
+ public getOracleDataForMarket(marketIndex: BN): OraclePriceData {
1569
+ const oracleKey = this.getMarketAccount(marketIndex).amm.oracle;
1570
+ const oracleData = this.getOraclePriceDataAndSlot(oracleKey).data;
1437
1571
 
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
- }
1534
-
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
- });
1572
+ return oracleData;
1542
1573
  }
1543
1574
  }