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

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