@drift-labs/sdk 0.1.29 → 0.1.30-master.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +2 -1
  2. package/lib/accounts/bulkAccountLoader.js +44 -32
  3. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +15 -0
  4. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +50 -23
  5. package/lib/clearingHouse.d.ts +0 -1
  6. package/lib/clearingHouse.js +0 -14
  7. package/lib/constants/accounts.d.ts +15 -0
  8. package/lib/constants/accounts.js +18 -0
  9. package/lib/factory/clearingHouse.d.ts +14 -4
  10. package/lib/factory/clearingHouse.js +23 -6
  11. package/lib/idl/clearing_house.json +2 -33
  12. package/lib/index.d.ts +0 -1
  13. package/lib/index.js +0 -1
  14. package/lib/math/amm.d.ts +20 -0
  15. package/lib/math/amm.js +151 -1
  16. package/lib/tx/retryTxSender.d.ts +5 -2
  17. package/lib/tx/retryTxSender.js +14 -1
  18. package/lib/util/promiseTimeout.d.ts +1 -0
  19. package/lib/util/promiseTimeout.js +14 -0
  20. package/package.json +1 -1
  21. package/src/accounts/bulkAccountLoader.ts +51 -40
  22. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +73 -30
  23. package/src/clearingHouse.ts +0 -13
  24. package/src/constants/accounts.ts +26 -0
  25. package/src/factory/clearingHouse.ts +47 -7
  26. package/src/idl/clearing_house.json +2 -33
  27. package/src/index.ts +0 -1
  28. package/src/math/amm.ts +212 -1
  29. package/src/tx/retryTxSender.ts +19 -1
  30. package/src/util/promiseTimeout.ts +14 -0
  31. package/lib/math/repeg.d.ts +0 -32
  32. package/lib/math/repeg.js +0 -178
  33. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -210
  34. package/src/accounts/pollingClearingHouseAccountSubscriber.js.map +0 -1
  35. package/src/accounts/pollingOracleSubscriber.js +0 -65
  36. package/src/accounts/pollingOracleSubscriber.js.map +0 -1
  37. package/src/accounts/pollingTokenAccountSubscriber.js +0 -65
  38. package/src/accounts/pollingTokenAccountSubscriber.js.map +0 -1
  39. package/src/accounts/utils.js +0 -8
  40. package/src/accounts/utils.js.map +0 -1
  41. package/src/accounts/webSocketAccountSubscriber.js +0 -64
  42. package/src/accounts/webSocketAccountSubscriber.js.map +0 -1
  43. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -212
  44. package/src/accounts/webSocketClearingHouseAccountSubscriber.js.map +0 -1
  45. package/src/assert/assert.js +0 -10
  46. package/src/assert/assert.js.map +0 -1
  47. package/src/constants/markets.js +0 -167
  48. package/src/constants/markets.js.map +0 -1
  49. package/src/constants/numericConstants.js +0 -22
  50. package/src/constants/numericConstants.js.map +0 -1
  51. package/src/math/conversion.js +0 -16
  52. package/src/math/conversion.js.map +0 -1
  53. package/src/math/funding.js +0 -223
  54. package/src/math/funding.js.map +0 -1
  55. package/src/math/insuranceFund.js +0 -23
  56. package/src/math/insuranceFund.js.map +0 -1
  57. package/src/math/market.js +0 -30
  58. package/src/math/market.js.map +0 -1
  59. package/src/math/orders.js +0 -73
  60. package/src/math/orders.js.map +0 -1
  61. package/src/math/position.js +0 -121
  62. package/src/math/position.js.map +0 -1
  63. package/src/math/repeg.ts +0 -253
  64. package/src/math/trade.js +0 -182
  65. package/src/math/trade.js.map +0 -1
  66. package/src/math/utils.js +0 -27
  67. package/src/math/utils.js.map +0 -1
  68. package/src/oracles/switchboardClient.js +0 -60
  69. package/src/oracles/switchboardClient.js.map +0 -1
  70. package/src/oracles/types.js +0 -3
  71. package/src/oracles/types.js.map +0 -1
  72. package/src/token/index.js +0 -39
  73. package/src/token/index.js.map +0 -1
  74. package/src/tx/defaultTxSender.js +0 -13
  75. package/src/tx/defaultTxSender.js.map +0 -1
  76. package/src/tx/retryTxSender.js +0 -137
  77. package/src/tx/retryTxSender.js.map +0 -1
  78. package/src/tx/types.js +0 -3
  79. package/src/tx/types.js.map +0 -1
  80. package/src/tx/utils.js +0 -9
  81. package/src/tx/utils.js.map +0 -1
  82. package/src/util/computeUnits.js +0 -17
  83. package/src/util/computeUnits.js.map +0 -1
  84. package/src/util/tps.js +0 -17
  85. package/src/util/tps.js.map +0 -1
@@ -24,6 +24,8 @@ import { getClearingHouseStateAccountPublicKey } from '../addresses';
24
24
  import { BulkAccountLoader } from './bulkAccountLoader';
25
25
  import { capitalize } from './utils';
26
26
  import { ClearingHouseConfigType } from '../factory/clearingHouse';
27
+ import { PublicKey } from '@solana/web3.js';
28
+ import { CLEARING_HOUSE_STATE_ACCOUNTS } from '../constants/accounts';
27
29
 
28
30
  export class PollingClearingHouseAccountSubscriber
29
31
  implements ClearingHouseAccountSubscriber
@@ -98,44 +100,38 @@ export class PollingClearingHouseAccountSubscriber
98
100
  return;
99
101
  }
100
102
 
101
- const statePublicKey = await getClearingHouseStateAccountPublicKey(
102
- this.program.programId
103
- );
104
-
105
- const state = (await this.program.account.state.fetch(
106
- statePublicKey
107
- )) as StateAccount;
103
+ const accounts = await this.getClearingHouseAccounts();
108
104
 
109
- this.accountsToPoll.set(statePublicKey.toString(), {
105
+ this.accountsToPoll.set(accounts.state.toString(), {
110
106
  key: 'state',
111
- publicKey: statePublicKey,
107
+ publicKey: accounts.state,
112
108
  eventType: 'stateAccountUpdate',
113
109
  });
114
110
 
115
- this.accountsToPoll.set(state.markets.toString(), {
111
+ this.accountsToPoll.set(accounts.markets.toString(), {
116
112
  key: 'markets',
117
- publicKey: state.markets,
113
+ publicKey: accounts.markets,
118
114
  eventType: 'marketsAccountUpdate',
119
115
  });
120
116
 
121
- this.accountsToPoll.set(state.orderState.toString(), {
117
+ this.accountsToPoll.set(accounts.orderState.toString(), {
122
118
  key: 'orderState',
123
- publicKey: state.orderState,
119
+ publicKey: accounts.orderState,
124
120
  eventType: 'orderStateAccountUpdate',
125
121
  });
126
122
 
127
123
  if (this.optionalExtraSubscriptions?.includes('tradeHistoryAccount')) {
128
- this.accountsToPoll.set(state.tradeHistory.toString(), {
124
+ this.accountsToPoll.set(accounts.tradeHistory.toString(), {
129
125
  key: 'tradeHistory',
130
- publicKey: state.tradeHistory,
126
+ publicKey: accounts.tradeHistory,
131
127
  eventType: 'tradeHistoryAccountUpdate',
132
128
  });
133
129
  }
134
130
 
135
131
  if (this.optionalExtraSubscriptions?.includes('depositHistoryAccount')) {
136
- this.accountsToPoll.set(state.depositHistory.toString(), {
132
+ this.accountsToPoll.set(accounts.depositHistory.toString(), {
137
133
  key: 'depositHistory',
138
- publicKey: state.depositHistory,
134
+ publicKey: accounts.depositHistory,
139
135
  eventType: 'depositHistoryAccountUpdate',
140
136
  });
141
137
  }
@@ -143,9 +139,9 @@ export class PollingClearingHouseAccountSubscriber
143
139
  if (
144
140
  this.optionalExtraSubscriptions?.includes('fundingPaymentHistoryAccount')
145
141
  ) {
146
- this.accountsToPoll.set(state.fundingPaymentHistory.toString(), {
142
+ this.accountsToPoll.set(accounts.fundingPaymentHistory.toString(), {
147
143
  key: 'fundingPaymentHistory',
148
- publicKey: state.fundingPaymentHistory,
144
+ publicKey: accounts.fundingPaymentHistory,
149
145
  eventType: 'fundingPaymentHistoryAccountUpdate',
150
146
  });
151
147
  }
@@ -153,17 +149,17 @@ export class PollingClearingHouseAccountSubscriber
153
149
  if (
154
150
  this.optionalExtraSubscriptions?.includes('fundingRateHistoryAccount')
155
151
  ) {
156
- this.accountsToPoll.set(state.fundingRateHistory.toString(), {
152
+ this.accountsToPoll.set(accounts.fundingRateHistory.toString(), {
157
153
  key: 'fundingRateHistory',
158
- publicKey: state.fundingRateHistory,
154
+ publicKey: accounts.fundingRateHistory,
159
155
  eventType: 'fundingRateHistoryAccountUpdate',
160
156
  });
161
157
  }
162
158
 
163
159
  if (this.optionalExtraSubscriptions?.includes('curveHistoryAccount')) {
164
- this.accountsToPoll.set(state.extendedCurveHistory.toString(), {
160
+ this.accountsToPoll.set(accounts.extendedCurveHistory.toString(), {
165
161
  key: 'extendedCurveHistory',
166
- publicKey: state.extendedCurveHistory,
162
+ publicKey: accounts.extendedCurveHistory,
167
163
  eventType: 'curveHistoryAccountUpdate',
168
164
  });
169
165
  }
@@ -171,24 +167,58 @@ export class PollingClearingHouseAccountSubscriber
171
167
  if (
172
168
  this.optionalExtraSubscriptions?.includes('liquidationHistoryAccount')
173
169
  ) {
174
- this.accountsToPoll.set(state.liquidationHistory.toString(), {
170
+ this.accountsToPoll.set(accounts.liquidationHistory.toString(), {
175
171
  key: 'liquidationHistory',
176
- publicKey: state.liquidationHistory,
172
+ publicKey: accounts.liquidationHistory,
177
173
  eventType: 'liquidationHistoryAccountUpdate',
178
174
  });
179
175
  }
180
176
 
177
+ if (this.optionalExtraSubscriptions?.includes('orderHistoryAccount')) {
178
+ this.accountsToPoll.set(accounts.orderHistory.toString(), {
179
+ key: 'orderHistory',
180
+ publicKey: accounts.orderHistory,
181
+ eventType: 'orderHistoryAccountUpdate',
182
+ });
183
+ }
184
+ }
185
+
186
+ async getClearingHouseAccounts(): Promise<ClearingHouseAccounts> {
187
+ // Skip extra calls to rpc if we already know all the accounts
188
+ if (CLEARING_HOUSE_STATE_ACCOUNTS[this.program.programId.toString()]) {
189
+ return CLEARING_HOUSE_STATE_ACCOUNTS[this.program.programId.toString()];
190
+ }
191
+
192
+ const statePublicKey = await getClearingHouseStateAccountPublicKey(
193
+ this.program.programId
194
+ );
195
+
196
+ const state = (await this.program.account.state.fetch(
197
+ statePublicKey
198
+ )) as StateAccount;
199
+
200
+ const accounts = {
201
+ state: statePublicKey,
202
+ markets: state.markets,
203
+ orderState: state.orderState,
204
+ tradeHistory: state.tradeHistory,
205
+ depositHistory: state.depositHistory,
206
+ fundingPaymentHistory: state.fundingPaymentHistory,
207
+ fundingRateHistory: state.fundingRateHistory,
208
+ extendedCurveHistory: state.extendedCurveHistory,
209
+ liquidationHistory: state.liquidationHistory,
210
+ orderHistory: undefined,
211
+ };
212
+
181
213
  if (this.optionalExtraSubscriptions?.includes('orderHistoryAccount')) {
182
214
  const orderState = (await this.program.account.orderState.fetch(
183
215
  state.orderState
184
216
  )) as OrderStateAccount;
185
217
 
186
- this.accountsToPoll.set(orderState.orderHistory.toString(), {
187
- key: 'orderHistory',
188
- publicKey: orderState.orderHistory,
189
- eventType: 'orderHistoryAccountUpdate',
190
- });
218
+ accounts.orderHistory = orderState.orderHistory;
191
219
  }
220
+
221
+ return accounts;
192
222
  }
193
223
 
194
224
  async addToAccountLoader(): Promise<void> {
@@ -324,3 +354,16 @@ export class PollingClearingHouseAccountSubscriber
324
354
  return this.orderHistory;
325
355
  }
326
356
  }
357
+
358
+ type ClearingHouseAccounts = {
359
+ state: PublicKey;
360
+ markets: PublicKey;
361
+ orderState: PublicKey;
362
+ tradeHistory?: PublicKey;
363
+ depositHistory?: PublicKey;
364
+ fundingPaymentHistory?: PublicKey;
365
+ fundingRateHistory?: PublicKey;
366
+ extendedCurveHistory?: PublicKey;
367
+ liquidationHistory?: PublicKey;
368
+ orderHistory?: PublicKey;
369
+ };
@@ -543,19 +543,6 @@ export class ClearingHouse {
543
543
  return [txSig, userAccountPublicKey];
544
544
  }
545
545
 
546
- public async deleteUser(): Promise<TransactionSignature> {
547
- const userAccountPublicKey = await this.getUserAccountPublicKey();
548
- const user = await this.program.account.user.fetch(userAccountPublicKey);
549
- const deleteUserTx = await this.program.transaction.deleteUser({
550
- accounts: {
551
- user: userAccountPublicKey,
552
- userPositions: user.positions,
553
- authority: this.wallet.publicKey,
554
- },
555
- });
556
- return this.txSender.send(deleteUserTx, [], this.opts);
557
- }
558
-
559
546
  public async withdrawCollateral(
560
547
  amount: BN,
561
548
  collateralAccountPublicKey: PublicKey
@@ -0,0 +1,26 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
3
+ export const CLEARING_HOUSE_STATE_ACCOUNTS = {
4
+ dammHkt7jmytvbS3nHTxQNEcP59aE57nxwV21YdqEDN: {
5
+ state: new PublicKey('FExhvPycCCwYnZGeDsVtLhpEQ3yEkVY2k1HuPyfLj91L'),
6
+ markets: new PublicKey('773hq3SbGPKVj93TXi5qV5CREuhxobywfALjS3XVHhLH'),
7
+ orderState: new PublicKey('4cC34bWwTPGncBaX2S6v5mH3Lj4Nb3byPMrxQSDcY985'),
8
+ tradeHistory: new PublicKey('FCuCXEoQppoaCYdttA7rK3HNQfYkTNEGpwuBESzYcENp'),
9
+ depositHistory: new PublicKey(
10
+ 'C7rF2Qy2rnDGQLRijBRRArJyaeuQcFi81BXDrUCQ45ya'
11
+ ),
12
+ fundingPaymentHistory: new PublicKey(
13
+ '895iPhzwT2tBufLnRpYtBG3gif1HDDUfpH2AqbS5joo4'
14
+ ),
15
+ fundingRateHistory: new PublicKey(
16
+ 'BWiJLMbmrwfqHVpcPJa8715XamNyNYamDDKQVQMnduEC'
17
+ ),
18
+ extendedCurveHistory: new PublicKey(
19
+ '7vBbqvMdtZLQdTVdzt5y63pZdAFE5W42kP3avngSJKCk'
20
+ ),
21
+ liquidationHistory: new PublicKey(
22
+ 'CSFaaf8yVoTx6NcXUKtNPYAewv76CH2jATqSVRBvUWKM'
23
+ ),
24
+ orderHistory: new PublicKey('DZ7XfUqyHoRKnJLRApxmJ943xHJ7NDBUTqpbooviEtbU'),
25
+ },
26
+ };
@@ -10,6 +10,7 @@ import { DefaultTxSender } from '../tx/defaultTxSender';
10
10
  import { ClearingHouseAccountSubscriber } from '../accounts/types';
11
11
  import { PollingClearingHouseAccountSubscriber } from '../accounts/pollingClearingHouseAccountSubscriber';
12
12
  import { Admin } from '../admin';
13
+ import { RetryTxSender } from '../tx/retryTxSender';
13
14
 
14
15
  export type ClearingHouseConfigType = 'websocket' | 'polling' | 'custom';
15
16
 
@@ -19,7 +20,7 @@ type BaseClearingHouseConfig = {
19
20
  wallet: IWallet;
20
21
  programID: PublicKey;
21
22
  opts?: ConfirmOptions;
22
- txSender?: TxSender;
23
+ txSenderConfig?: TxSenderConfig;
23
24
  };
24
25
 
25
26
  type WebSocketClearingHouseConfiguration = BaseClearingHouseConfig;
@@ -32,12 +33,28 @@ type ClearingHouseConfig =
32
33
  | PollingClearingHouseConfiguration
33
34
  | WebSocketClearingHouseConfiguration;
34
35
 
36
+ export type TxSenderType = 'default' | 'retry';
37
+
38
+ type BaseTxSenderConfig = {
39
+ type: TxSenderType;
40
+ };
41
+
42
+ type DefaultTxSenderConfig = BaseTxSenderConfig;
43
+
44
+ type RetryTxSenderConfig = BaseTxSenderConfig & {
45
+ timeout?: number;
46
+ retrySleep?: number;
47
+ additionalConnections?: Connection[];
48
+ };
49
+
50
+ type TxSenderConfig = DefaultTxSenderConfig | RetryTxSenderConfig;
51
+
35
52
  export function getWebSocketClearingHouseConfig(
36
53
  connection: Connection,
37
54
  wallet: IWallet,
38
55
  programID: PublicKey,
39
56
  opts: ConfirmOptions = Provider.defaultOptions(),
40
- txSender?: TxSender
57
+ txSenderConfig?: TxSenderConfig
41
58
  ): WebSocketClearingHouseConfiguration {
42
59
  return {
43
60
  type: 'websocket',
@@ -45,7 +62,7 @@ export function getWebSocketClearingHouseConfig(
45
62
  wallet,
46
63
  programID,
47
64
  opts,
48
- txSender,
65
+ txSenderConfig,
49
66
  };
50
67
  }
51
68
 
@@ -55,7 +72,7 @@ export function getPollingClearingHouseConfig(
55
72
  programID: PublicKey,
56
73
  accountLoader: BulkAccountLoader,
57
74
  opts: ConfirmOptions = Provider.defaultOptions(),
58
- txSender?: TxSender
75
+ txSenderConfig?: TxSenderConfig
59
76
  ): PollingClearingHouseConfiguration {
60
77
  return {
61
78
  type: 'polling',
@@ -64,7 +81,7 @@ export function getPollingClearingHouseConfig(
64
81
  programID,
65
82
  accountLoader,
66
83
  opts,
67
- txSender,
84
+ txSenderConfig,
68
85
  };
69
86
  }
70
87
 
@@ -85,7 +102,19 @@ export function getClearingHouse(config: ClearingHouseConfig): ClearingHouse {
85
102
  );
86
103
  }
87
104
 
88
- const txSender = config.txSender || new DefaultTxSender(provider);
105
+ let txSender: TxSender;
106
+ if (config.txSenderConfig?.type === 'retry') {
107
+ const txSenderConfig = config.txSenderConfig as RetryTxSenderConfig;
108
+ txSender = new RetryTxSender(
109
+ provider,
110
+ txSenderConfig.timeout,
111
+ txSenderConfig.retrySleep,
112
+ txSenderConfig.additionalConnections
113
+ );
114
+ } else {
115
+ txSender = new DefaultTxSender(provider);
116
+ }
117
+
89
118
  return new ClearingHouse(
90
119
  config.connection,
91
120
  config.wallet,
@@ -113,7 +142,18 @@ export function getAdmin(config: ClearingHouseConfig): Admin {
113
142
  );
114
143
  }
115
144
 
116
- const txSender = config.txSender || new DefaultTxSender(provider);
145
+ let txSender: TxSender;
146
+ if (config.txSenderConfig?.type === 'retry') {
147
+ const txSenderConfig = config.txSenderConfig as RetryTxSenderConfig;
148
+ txSender = new RetryTxSender(
149
+ provider,
150
+ txSenderConfig.timeout,
151
+ txSenderConfig.retrySleep,
152
+ txSenderConfig.additionalConnections
153
+ );
154
+ } else {
155
+ txSender = new DefaultTxSender(provider);
156
+ }
117
157
  return new Admin(
118
158
  config.connection,
119
159
  config.wallet,
@@ -1482,32 +1482,6 @@
1482
1482
  }
1483
1483
  ]
1484
1484
  },
1485
- {
1486
- "name": "deleteUser",
1487
- "accounts": [
1488
- {
1489
- "name": "user",
1490
- "isMut": true,
1491
- "isSigner": false
1492
- },
1493
- {
1494
- "name": "userPositions",
1495
- "isMut": true,
1496
- "isSigner": false
1497
- },
1498
- {
1499
- "name": "userOrders",
1500
- "isMut": true,
1501
- "isSigner": false
1502
- },
1503
- {
1504
- "name": "authority",
1505
- "isMut": false,
1506
- "isSigner": true
1507
- }
1508
- ],
1509
- "args": []
1510
- },
1511
1485
  {
1512
1486
  "name": "settleFundingPayment",
1513
1487
  "accounts": [
@@ -3292,8 +3266,8 @@
3292
3266
  "type": "u128"
3293
3267
  },
3294
3268
  {
3295
- "name": "netRevenueSinceLastFunding",
3296
- "type": "i64"
3269
+ "name": "padding1",
3270
+ "type": "u64"
3297
3271
  },
3298
3272
  {
3299
3273
  "name": "padding2",
@@ -4300,11 +4274,6 @@
4300
4274
  "code": 6060,
4301
4275
  "name": "CantExpireOrders",
4302
4276
  "msg": "CantExpireOrders"
4303
- },
4304
- {
4305
- "code": 6061,
4306
- "name": "InvalidRepegPriceImpact",
4307
- "msg": "AMM repeg mark price impact vs oracle too large"
4308
4277
  }
4309
4278
  ]
4310
4279
  }
package/src/index.ts CHANGED
@@ -29,7 +29,6 @@ export * from './math/position';
29
29
  export * from './math/amm';
30
30
  export * from './math/trade';
31
31
  export * from './math/orders';
32
- export * from './math/repeg';
33
32
  export * from './orders';
34
33
  export * from './orderParams';
35
34
  export * from './wallet';
package/src/math/amm.ts CHANGED
@@ -4,7 +4,11 @@ import {
4
4
  MARK_PRICE_PRECISION,
5
5
  PEG_PRECISION,
6
6
  ZERO,
7
+ AMM_TO_QUOTE_PRECISION_RATIO,
8
+ QUOTE_PRECISION,
9
+ AMM_RESERVE_PRECISION,
7
10
  } from '../constants/numericConstants';
11
+ import { calculateBaseAssetValue } from './position';
8
12
  import {
9
13
  AMM,
10
14
  PositionDirection,
@@ -13,7 +17,12 @@ import {
13
17
  isVariant,
14
18
  } from '../types';
15
19
  import { assert } from '../assert/assert';
16
- import { squareRootBN } from '..';
20
+ import {
21
+ calculatePositionPNL,
22
+ calculateMarkPrice,
23
+ convertToNumber,
24
+ squareRootBN,
25
+ } from '..';
17
26
 
18
27
  /**
19
28
  * Calculates a price given an arbitrary base and quote amount (they must have the same precision)
@@ -130,6 +139,125 @@ export function getSwapDirection(
130
139
  return SwapDirection.ADD;
131
140
  }
132
141
 
142
+ /**
143
+ * Helper function calculating adjust k cost
144
+ * @param market
145
+ * @param marketIndex
146
+ * @param numerator
147
+ * @param denomenator
148
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
149
+ */
150
+ export function calculateAdjustKCost(
151
+ market: Market,
152
+ marketIndex: BN,
153
+ numerator: BN,
154
+ denomenator: BN
155
+ ): BN {
156
+ const netUserPosition = {
157
+ baseAssetAmount: market.baseAssetAmount,
158
+ lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
159
+ marketIndex: new BN(marketIndex),
160
+ quoteAssetAmount: new BN(0),
161
+ openOrders: new BN(0),
162
+ };
163
+
164
+ const currentValue = calculateBaseAssetValue(market, netUserPosition);
165
+
166
+ const marketNewK = Object.assign({}, market);
167
+ marketNewK.amm = Object.assign({}, market.amm);
168
+
169
+ marketNewK.amm.baseAssetReserve = market.amm.baseAssetReserve
170
+ .mul(numerator)
171
+ .div(denomenator);
172
+ marketNewK.amm.quoteAssetReserve = market.amm.quoteAssetReserve
173
+ .mul(numerator)
174
+ .div(denomenator);
175
+ marketNewK.amm.sqrtK = market.amm.sqrtK.mul(numerator).div(denomenator);
176
+
177
+ netUserPosition.quoteAssetAmount = currentValue;
178
+
179
+ const cost = calculatePositionPNL(marketNewK, netUserPosition);
180
+
181
+ const p = PEG_PRECISION.mul(numerator).div(denomenator);
182
+ const x = market.amm.baseAssetReserve;
183
+ const y = market.amm.quoteAssetReserve;
184
+ const delta = market.baseAssetAmount;
185
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
186
+
187
+ const numer1 = PEG_PRECISION.sub(p).mul(y).div(PEG_PRECISION);
188
+ const numer20 = k
189
+ .mul(p)
190
+ .mul(p)
191
+ .div(PEG_PRECISION)
192
+ .div(PEG_PRECISION)
193
+ .div(x.mul(p).div(PEG_PRECISION).add(delta));
194
+ const numer21 = k.div(x.add(delta));
195
+
196
+ const formulaCost = numer21
197
+ .sub(numer20)
198
+ .sub(numer1)
199
+ .mul(market.amm.pegMultiplier)
200
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
201
+ console.log(convertToNumber(formulaCost, QUOTE_PRECISION));
202
+
203
+ // p.div(p.mul(x).add(delta)).sub()
204
+
205
+ return cost;
206
+ }
207
+
208
+ /**
209
+ * Helper function calculating adjust pegMultiplier (repeg) cost
210
+ *
211
+ * @param market
212
+ * @param marketIndex
213
+ * @param newPeg
214
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
215
+ */
216
+ export function calculateRepegCost(
217
+ market: Market,
218
+ marketIndex: BN,
219
+ newPeg: BN
220
+ ): BN {
221
+ const netUserPosition = {
222
+ baseAssetAmount: market.baseAssetAmount,
223
+ lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
224
+ marketIndex: new BN(marketIndex),
225
+ quoteAssetAmount: new BN(0),
226
+ openOrders: new BN(0),
227
+ };
228
+
229
+ const currentValue = calculateBaseAssetValue(market, netUserPosition);
230
+ netUserPosition.quoteAssetAmount = currentValue;
231
+ const prevMarketPrice = calculateMarkPrice(market);
232
+ const marketNewPeg = Object.assign({}, market);
233
+ marketNewPeg.amm = Object.assign({}, market.amm);
234
+
235
+ // const marketNewPeg = JSON.parse(JSON.stringify(market));
236
+ marketNewPeg.amm.pegMultiplier = newPeg;
237
+
238
+ console.log(
239
+ 'Price moves from',
240
+ convertToNumber(prevMarketPrice),
241
+ 'to',
242
+ convertToNumber(calculateMarkPrice(marketNewPeg))
243
+ );
244
+
245
+ const cost = calculatePositionPNL(marketNewPeg, netUserPosition);
246
+
247
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
248
+ const newQuoteAssetReserve = k.div(
249
+ market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount)
250
+ );
251
+ const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(
252
+ market.amm.quoteAssetReserve
253
+ );
254
+ const cost2 = deltaQuoteAssetReserves
255
+ .mul(market.amm.pegMultiplier.sub(newPeg))
256
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
257
+ console.log(convertToNumber(cost2, QUOTE_PRECISION));
258
+ return cost;
259
+ }
260
+
133
261
  /**
134
262
  * Helper function calculating terminal price of amm
135
263
  *
@@ -187,3 +315,86 @@ export function calculateMaxBaseAssetAmountToTrade(
187
315
  return [new BN(0), PositionDirection.LONG];
188
316
  }
189
317
  }
318
+
319
+ export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] {
320
+ // wolframalpha.com
321
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
322
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
323
+
324
+ // todo: assumes k = x * y
325
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
326
+
327
+ // const k = market.amm.sqrtK.mul(market.amm.sqrtK);
328
+ const x = market.amm.baseAssetReserve;
329
+ const y = market.amm.quoteAssetReserve;
330
+
331
+ const d = market.baseAssetAmount;
332
+ const Q = market.amm.pegMultiplier;
333
+
334
+ const C = cost.mul(new BN(-1));
335
+
336
+ const numer1 = y.mul(d).mul(Q).div(AMM_RESERVE_PRECISION).div(PEG_PRECISION);
337
+ const numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION);
338
+ const denom1 = C.mul(x)
339
+ .mul(x.add(d))
340
+ .div(AMM_RESERVE_PRECISION)
341
+ .div(QUOTE_PRECISION);
342
+ const denom2 = y
343
+ .mul(d)
344
+ .mul(d)
345
+ .mul(Q)
346
+ .div(AMM_RESERVE_PRECISION)
347
+ .div(AMM_RESERVE_PRECISION)
348
+ .div(PEG_PRECISION);
349
+
350
+ const numerator = d
351
+ .mul(numer1.add(numer2))
352
+ .div(AMM_RESERVE_PRECISION)
353
+ .div(AMM_RESERVE_PRECISION)
354
+ .div(AMM_TO_QUOTE_PRECISION_RATIO);
355
+ const denominator = denom1
356
+ .add(denom2)
357
+ .div(AMM_RESERVE_PRECISION)
358
+ .div(AMM_TO_QUOTE_PRECISION_RATIO);
359
+ console.log(numerator, denominator);
360
+ // const p = (numerator).div(denominator);
361
+
362
+ // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
363
+ // console.log(convertToNumber(formulaCost, QUOTE_PRECISION))
364
+
365
+ return [numerator, denominator];
366
+ }
367
+
368
+ export function calculateBudgetedPeg(market: Market, cost: BN): BN {
369
+ // wolframalpha.com
370
+ // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
371
+ // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
372
+
373
+ // todo: assumes k = x * y
374
+ // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
375
+
376
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
377
+ const x = market.amm.baseAssetReserve;
378
+ const y = market.amm.quoteAssetReserve;
379
+
380
+ const d = market.baseAssetAmount;
381
+ const Q = market.amm.pegMultiplier;
382
+
383
+ const C = cost.mul(new BN(-1));
384
+
385
+ const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
386
+ const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION)
387
+ .div(deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO))
388
+ .mul(PEG_PRECISION)
389
+ .div(QUOTE_PRECISION);
390
+ console.log(
391
+ Q.toNumber(),
392
+ 'change by',
393
+ deltaPegMultiplier.toNumber() / MARK_PRICE_PRECISION.toNumber()
394
+ );
395
+ const newPeg = Q.sub(
396
+ deltaPegMultiplier.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION)
397
+ );
398
+
399
+ return newPeg;
400
+ }
@@ -8,6 +8,7 @@ import {
8
8
  SignatureResult,
9
9
  Transaction,
10
10
  TransactionSignature,
11
+ Connection,
11
12
  } from '@solana/web3.js';
12
13
  import { Provider } from '@project-serum/anchor';
13
14
  import assert from 'assert';
@@ -24,15 +25,18 @@ export class RetryTxSender implements TxSender {
24
25
  provider: Provider;
25
26
  timeout: number;
26
27
  retrySleep: number;
28
+ additionalConnections: Connection[];
27
29
 
28
30
  public constructor(
29
31
  provider: Provider,
30
32
  timeout?: number,
31
- retrySleep?: number
33
+ retrySleep?: number,
34
+ additionalConnections = new Array<Connection>()
32
35
  ) {
33
36
  this.provider = provider;
34
37
  this.timeout = timeout ?? DEFAULT_TIMEOUT;
35
38
  this.retrySleep = retrySleep ?? DEFAULT_RETRY;
39
+ this.additionalConnections = additionalConnections;
36
40
  }
37
41
 
38
42
  async send(
@@ -54,6 +58,7 @@ export class RetryTxSender implements TxSender {
54
58
 
55
59
  const txid: TransactionSignature =
56
60
  await this.provider.connection.sendRawTransaction(rawTransaction, opts);
61
+ this.sendToAdditionalConnections(rawTransaction, opts);
57
62
 
58
63
  let done = false;
59
64
  const resolveReference: ResolveReference = {
@@ -76,6 +81,7 @@ export class RetryTxSender implements TxSender {
76
81
  console.error(e);
77
82
  stopWaiting();
78
83
  });
84
+ this.sendToAdditionalConnections(rawTransaction, opts);
79
85
  }
80
86
  }
81
87
  })();
@@ -193,4 +199,16 @@ export class RetryTxSender implements TxSender {
193
199
  return result;
194
200
  });
195
201
  }
202
+
203
+ sendToAdditionalConnections(rawTx: Buffer, opts: ConfirmOptions): void {
204
+ this.additionalConnections.map((connection) => {
205
+ connection.sendRawTransaction(rawTx, opts).catch((e) => {
206
+ console.error(
207
+ // @ts-ignore
208
+ `error sending tx to additional connection ${connection._rpcEndpoint}`
209
+ );
210
+ console.error(e);
211
+ });
212
+ });
213
+ }
196
214
  }