@drift-labs/sdk 0.2.0-master.10 → 0.2.0-master.13

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 (82) hide show
  1. package/lib/admin.d.ts +3 -3
  2. package/lib/admin.js +6 -6
  3. package/lib/clearingHouse.d.ts +22 -6
  4. package/lib/clearingHouse.js +357 -74
  5. package/lib/clearingHouseUser.d.ts +12 -17
  6. package/lib/clearingHouseUser.js +97 -88
  7. package/lib/config.js +1 -1
  8. package/lib/constants/banks.d.ts +2 -2
  9. package/lib/constants/banks.js +5 -4
  10. package/lib/constants/numericConstants.d.ts +3 -0
  11. package/lib/constants/numericConstants.js +4 -1
  12. package/lib/events/eventList.js +3 -0
  13. package/lib/events/types.d.ts +2 -1
  14. package/lib/factory/bigNum.d.ts +1 -0
  15. package/lib/factory/bigNum.js +37 -11
  16. package/lib/idl/clearing_house.json +611 -146
  17. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  18. package/lib/index.d.ts +2 -1
  19. package/lib/index.js +2 -1
  20. package/lib/math/amm.js +2 -2
  21. package/lib/math/bankBalance.d.ts +3 -1
  22. package/lib/math/bankBalance.js +54 -1
  23. package/lib/math/margin.d.ts +11 -0
  24. package/lib/math/margin.js +72 -0
  25. package/lib/math/market.d.ts +4 -1
  26. package/lib/math/market.js +35 -1
  27. package/lib/math/position.d.ts +8 -0
  28. package/lib/math/position.js +42 -12
  29. package/lib/orders.d.ts +1 -2
  30. package/lib/orders.js +2 -77
  31. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  32. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  33. package/lib/tx/retryTxSender.js +9 -2
  34. package/lib/types.d.ts +86 -16
  35. package/lib/types.js +18 -1
  36. package/lib/util/computeUnits.js +1 -1
  37. package/lib/util/getTokenAddress.d.ts +2 -0
  38. package/lib/util/getTokenAddress.js +9 -0
  39. package/package.json +1 -1
  40. package/src/admin.ts +7 -7
  41. package/src/clearingHouse.ts +636 -94
  42. package/src/clearingHouseConfig.js +2 -0
  43. package/src/clearingHouseUser.ts +219 -121
  44. package/src/clearingHouseUserConfig.js +2 -0
  45. package/src/config.ts +1 -1
  46. package/src/constants/banks.js +42 -0
  47. package/src/constants/banks.ts +7 -4
  48. package/src/constants/markets.js +42 -0
  49. package/src/constants/numericConstants.js +41 -0
  50. package/src/constants/numericConstants.ts +4 -0
  51. package/src/events/eventList.ts +3 -0
  52. package/src/events/types.ts +2 -0
  53. package/src/factory/bigNum.js +37 -11
  54. package/src/factory/bigNum.ts +43 -13
  55. package/src/idl/clearing_house.json +611 -146
  56. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  57. package/src/index.js +1 -1
  58. package/src/index.ts +2 -1
  59. package/src/math/amm.ts +8 -5
  60. package/src/math/bankBalance.ts +98 -1
  61. package/src/math/margin.ts +124 -0
  62. package/src/math/market.ts +66 -1
  63. package/src/math/position.ts +59 -9
  64. package/src/mockUSDCFaucet.js +276 -167
  65. package/src/orders.ts +4 -157
  66. package/src/tokenFaucet.js +189 -0
  67. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  68. package/src/tx/retryTxSender.ts +11 -3
  69. package/src/types.js +12 -1
  70. package/src/types.ts +88 -16
  71. package/src/{accounts/fetch.js → util/computeUnits.js} +11 -13
  72. package/src/util/computeUnits.ts +1 -1
  73. package/src/util/getTokenAddress.js +9 -0
  74. package/src/util/getTokenAddress.ts +18 -0
  75. package/tests/bn/test.ts +2 -0
  76. package/src/addresses/pda.js +0 -104
  77. package/src/math/bankBalance.js +0 -75
  78. package/src/math/market.js +0 -57
  79. package/src/math/orders.js +0 -110
  80. package/src/math/position.js +0 -140
  81. package/src/orders.js +0 -134
  82. package/src/tx/retryTxSender.js +0 -188
@@ -1,23 +1,23 @@
1
1
  {
2
- "version": "0.0.0",
3
- "name": "mock_usdc_faucet",
2
+ "version": "0.1.0",
3
+ "name": "token_faucet",
4
4
  "instructions": [
5
5
  {
6
6
  "name": "initialize",
7
7
  "accounts": [
8
8
  {
9
- "name": "mockUsdcFaucetState",
9
+ "name": "faucetConfig",
10
10
  "isMut": true,
11
11
  "isSigner": false
12
12
  },
13
13
  {
14
14
  "name": "admin",
15
- "isMut": false,
15
+ "isMut": true,
16
16
  "isSigner": true
17
17
  },
18
18
  {
19
19
  "name": "mintAccount",
20
- "isMut": false,
20
+ "isMut": true,
21
21
  "isSigner": false
22
22
  },
23
23
  {
@@ -29,20 +29,20 @@
29
29
  "name": "systemProgram",
30
30
  "isMut": false,
31
31
  "isSigner": false
32
- }
33
- ],
34
- "args": [
32
+ },
35
33
  {
36
- "name": "mockUsdcFaucetNonce",
37
- "type": "u8"
34
+ "name": "tokenProgram",
35
+ "isMut": false,
36
+ "isSigner": false
38
37
  }
39
- ]
38
+ ],
39
+ "args": []
40
40
  },
41
41
  {
42
42
  "name": "mintToUser",
43
43
  "accounts": [
44
44
  {
45
- "name": "mockUsdcFaucetState",
45
+ "name": "faucetConfig",
46
46
  "isMut": false,
47
47
  "isSigner": false
48
48
  },
@@ -73,11 +73,42 @@
73
73
  "type": "u64"
74
74
  }
75
75
  ]
76
+ },
77
+ {
78
+ "name": "transferMintAuthority",
79
+ "accounts": [
80
+ {
81
+ "name": "faucetConfig",
82
+ "isMut": false,
83
+ "isSigner": false
84
+ },
85
+ {
86
+ "name": "admin",
87
+ "isMut": true,
88
+ "isSigner": true
89
+ },
90
+ {
91
+ "name": "mintAccount",
92
+ "isMut": true,
93
+ "isSigner": false
94
+ },
95
+ {
96
+ "name": "mintAuthority",
97
+ "isMut": false,
98
+ "isSigner": false
99
+ },
100
+ {
101
+ "name": "tokenProgram",
102
+ "isMut": false,
103
+ "isSigner": false
104
+ }
105
+ ],
106
+ "args": []
76
107
  }
77
108
  ],
78
109
  "accounts": [
79
110
  {
80
- "name": "MockUSDCFaucetState",
111
+ "name": "FaucetConfig",
81
112
  "type": {
82
113
  "kind": "struct",
83
114
  "fields": [
@@ -103,17 +134,9 @@
103
134
  ],
104
135
  "errors": [
105
136
  {
106
- "code": 300,
137
+ "code": 6000,
107
138
  "name": "InvalidMintAccountAuthority",
108
139
  "msg": "Program not mint authority"
109
- },
110
- {
111
- "code": 301,
112
- "name": "Unauthorized",
113
- "msg": "Signer must be MockUSDCFaucet admin"
114
140
  }
115
- ],
116
- "metadata": {
117
- "address": "2z2DLVD3tBWc86pbvvy5qN31v1NXprM6zA5MDr2FMx64"
118
- }
141
+ ]
119
142
  }
package/src/index.js CHANGED
@@ -20,7 +20,7 @@ const web3_js_1 = require("@solana/web3.js");
20
20
  Object.defineProperty(exports, "PublicKey", { enumerable: true, get: function () { return web3_js_1.PublicKey; } });
21
21
  const client_1 = __importDefault(require("@pythnetwork/client"));
22
22
  exports.pyth = client_1.default;
23
- __exportStar(require("./mockUSDCFaucet"), exports);
23
+ __exportStar(require("./tokenFaucet"), exports);
24
24
  __exportStar(require("./oracles/types"), exports);
25
25
  __exportStar(require("./oracles/pythClient"), exports);
26
26
  __exportStar(require("./oracles/switchboardClient"), exports);
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@ import { BN } from '@project-serum/anchor';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  import pyth from '@pythnetwork/client';
4
4
 
5
- export * from './mockUSDCFaucet';
5
+ export * from './tokenFaucet';
6
6
  export * from './oracles/types';
7
7
  export * from './oracles/pythClient';
8
8
  export * from './oracles/switchboardClient';
@@ -35,6 +35,7 @@ export * from './math/amm';
35
35
  export * from './math/trade';
36
36
  export * from './math/orders';
37
37
  export * from './math/repeg';
38
+ export * from './math/margin';
38
39
  export * from './orders';
39
40
  export * from './orderParams';
40
41
  export * from './slot/SlotSubscriber';
package/src/math/amm.ts CHANGED
@@ -33,11 +33,14 @@ export function calculatePegFromTargetPrice(
33
33
  baseAssetReserve: BN,
34
34
  quoteAssetReserve: BN
35
35
  ): BN {
36
- return targetPrice
37
- .mul(baseAssetReserve)
38
- .div(quoteAssetReserve)
39
- .add(PRICE_DIV_PEG.div(new BN(2)))
40
- .div(PRICE_DIV_PEG);
36
+ return BN.max(
37
+ targetPrice
38
+ .mul(baseAssetReserve)
39
+ .div(quoteAssetReserve)
40
+ .add(PRICE_DIV_PEG.div(new BN(2)))
41
+ .div(PRICE_DIV_PEG),
42
+ ONE
43
+ );
41
44
  }
42
45
 
43
46
  export function calculateOptimalPegAndBudget(
@@ -1,4 +1,9 @@
1
- import { BankAccount, BankBalanceType, isVariant } from '../types';
1
+ import {
2
+ BankAccount,
3
+ BankBalanceType,
4
+ isVariant,
5
+ MarginCategory,
6
+ } from '../types';
2
7
  import { BN } from '@project-serum/anchor';
3
8
  import {
4
9
  BANK_UTILIZATION_PRECISION,
@@ -6,8 +11,14 @@ import {
6
11
  TEN,
7
12
  ZERO,
8
13
  BANK_INTEREST_PRECISION,
14
+ BANK_WEIGHT_PRECISION,
9
15
  ONE_YEAR,
16
+ AMM_RESERVE_PRECISION,
10
17
  } from '../constants/numericConstants';
18
+ import {
19
+ calculateSizeDiscountAssetWeight,
20
+ calculateSizePremiumLiabilityWeight,
21
+ } from './margin';
11
22
 
12
23
  export function getBalance(
13
24
  tokenAmount: BN,
@@ -43,6 +54,92 @@ export function getTokenAmount(
43
54
  return balanceAmount.mul(cumulativeInterest).div(precisionDecrease);
44
55
  }
45
56
 
57
+ export function calculateAssetWeight(
58
+ balanceAmount: BN,
59
+ bank: BankAccount,
60
+ marginCategory: MarginCategory
61
+ ): BN {
62
+ const sizePrecision = TEN.pow(new BN(bank.decimals));
63
+ let sizeInAmmReservePrecision;
64
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
65
+ sizeInAmmReservePrecision = balanceAmount.div(
66
+ sizePrecision.div(AMM_RESERVE_PRECISION)
67
+ );
68
+ } else {
69
+ sizeInAmmReservePrecision = balanceAmount
70
+ .mul(AMM_RESERVE_PRECISION)
71
+ .div(sizePrecision);
72
+ }
73
+
74
+ let assetWeight;
75
+
76
+ switch (marginCategory) {
77
+ case 'Initial':
78
+ assetWeight = calculateSizeDiscountAssetWeight(
79
+ sizeInAmmReservePrecision,
80
+ bank.imfFactor,
81
+ bank.initialAssetWeight
82
+ );
83
+ break;
84
+ case 'Maintenance':
85
+ assetWeight = calculateSizeDiscountAssetWeight(
86
+ sizeInAmmReservePrecision,
87
+ bank.imfFactor,
88
+ bank.maintenanceAssetWeight
89
+ );
90
+ break;
91
+ default:
92
+ assetWeight = bank.initialAssetWeight;
93
+ break;
94
+ }
95
+
96
+ return assetWeight;
97
+ }
98
+
99
+ export function calculateLiabilityWeight(
100
+ balanceAmount: BN,
101
+ bank: BankAccount,
102
+ marginCategory: MarginCategory
103
+ ): BN {
104
+ const sizePrecision = TEN.pow(new BN(bank.decimals));
105
+ let sizeInAmmReservePrecision;
106
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
107
+ sizeInAmmReservePrecision = balanceAmount.div(
108
+ sizePrecision.div(AMM_RESERVE_PRECISION)
109
+ );
110
+ } else {
111
+ sizeInAmmReservePrecision = balanceAmount
112
+ .mul(AMM_RESERVE_PRECISION)
113
+ .div(sizePrecision);
114
+ }
115
+
116
+ let assetWeight;
117
+
118
+ switch (marginCategory) {
119
+ case 'Initial':
120
+ assetWeight = calculateSizePremiumLiabilityWeight(
121
+ sizeInAmmReservePrecision,
122
+ bank.imfFactor,
123
+ bank.initialLiabilityWeight,
124
+ BANK_WEIGHT_PRECISION
125
+ );
126
+ break;
127
+ case 'Maintenance':
128
+ assetWeight = calculateSizePremiumLiabilityWeight(
129
+ sizeInAmmReservePrecision,
130
+ bank.imfFactor,
131
+ bank.maintenanceLiabilityWeight,
132
+ BANK_WEIGHT_PRECISION
133
+ );
134
+ break;
135
+ default:
136
+ assetWeight = bank.initialLiabilityWeight;
137
+ break;
138
+ }
139
+
140
+ return assetWeight;
141
+ }
142
+
46
143
  export function calculateInterestAccumulated(
47
144
  bank: BankAccount,
48
145
  now: BN
@@ -0,0 +1,124 @@
1
+ import { squareRootBN } from './utils';
2
+ import {
3
+ BANK_WEIGHT_PRECISION,
4
+ BANK_IMF_PRECISION,
5
+ ZERO,
6
+ BID_ASK_SPREAD_PRECISION,
7
+ AMM_TO_QUOTE_PRECISION_RATIO,
8
+ MARK_PRICE_PRECISION,
9
+ } from '../constants/numericConstants';
10
+ import { BN } from '@project-serum/anchor';
11
+ import { OraclePriceData } from '../oracles/types';
12
+ import { MarketAccount, UserPosition } from '..';
13
+
14
+ export function calculateSizePremiumLiabilityWeight(
15
+ size: BN, // AMM_RESERVE_PRECISION
16
+ imfFactor: BN,
17
+ liabilityWeight: BN,
18
+ precision: BN
19
+ ): BN {
20
+ if (imfFactor.eq(ZERO)) {
21
+ return liabilityWeight;
22
+ }
23
+
24
+ const sizeSqrt = squareRootBN(size.div(new BN(1000)).add(new BN(1))); //1e13 -> 1e10 -> 1e5
25
+ const liabilityWeightNumerator = liabilityWeight.sub(
26
+ liabilityWeight.div(BN.max(new BN(1), BANK_IMF_PRECISION.div(imfFactor)))
27
+ );
28
+
29
+ const sizePremiumLiabilityWeight = liabilityWeightNumerator.add(
30
+ sizeSqrt // 1e5
31
+ .mul(imfFactor)
32
+ .div(new BN(100_000).mul(BANK_IMF_PRECISION).div(precision)) // 1e5
33
+ );
34
+
35
+ const maxLiabilityWeight = BN.max(
36
+ liabilityWeight,
37
+ sizePremiumLiabilityWeight
38
+ );
39
+ return maxLiabilityWeight;
40
+ }
41
+
42
+ export function calculateSizeDiscountAssetWeight(
43
+ size: BN, // AMM_RESERVE_PRECISION
44
+ imfFactor: BN,
45
+ assetWeight: BN
46
+ ): BN {
47
+ if (imfFactor.eq(ZERO)) {
48
+ return assetWeight;
49
+ }
50
+
51
+ const sizeSqrt = squareRootBN(size.div(new BN(1000)).add(new BN(1))); //1e13 -> 1e10 -> 1e5
52
+ const imfNumerator = BANK_IMF_PRECISION.add(
53
+ BANK_IMF_PRECISION.div(new BN(10))
54
+ );
55
+
56
+ const sizeDiscountAssetWeight = imfNumerator.mul(BANK_WEIGHT_PRECISION).div(
57
+ BANK_IMF_PRECISION.add(
58
+ sizeSqrt // 1e5
59
+ .mul(imfFactor)
60
+ .div(new BN(100_000)) // 1e5
61
+ )
62
+ );
63
+
64
+ const minAssetWeight = BN.min(assetWeight, sizeDiscountAssetWeight);
65
+
66
+ return minAssetWeight;
67
+ }
68
+
69
+ export function calculateOraclePriceForPerpMargin(
70
+ marketPosition: UserPosition,
71
+ market: MarketAccount,
72
+ oraclePriceData: OraclePriceData
73
+ ): BN {
74
+ const oraclePriceOffset = BN.min(
75
+ new BN(market.amm.maxSpread)
76
+ .mul(oraclePriceData.price)
77
+ .div(BID_ASK_SPREAD_PRECISION),
78
+ oraclePriceData.confidence.add(
79
+ new BN(market.amm.baseSpread)
80
+ .mul(oraclePriceData.price)
81
+ .div(BID_ASK_SPREAD_PRECISION)
82
+ )
83
+ );
84
+
85
+ let marginPrice: BN;
86
+ if (marketPosition.baseAssetAmount.gt(ZERO)) {
87
+ marginPrice = oraclePriceData.price.sub(oraclePriceOffset);
88
+ } else {
89
+ marginPrice = oraclePriceData.price.add(oraclePriceOffset);
90
+ }
91
+
92
+ return marginPrice;
93
+ }
94
+
95
+ export function calculateMarginBaseAssetValue(
96
+ market: MarketAccount,
97
+ marketPosition: UserPosition,
98
+ oraclePriceData: OraclePriceData
99
+ ): BN {
100
+ const marginPrice = calculateOraclePriceForPerpMargin(
101
+ marketPosition,
102
+ market,
103
+ oraclePriceData
104
+ );
105
+ const baseAssetValue = marketPosition.baseAssetAmount
106
+ .abs()
107
+ .mul(marginPrice)
108
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
109
+
110
+ return baseAssetValue;
111
+ }
112
+
113
+ export function calculateWorstCaseBaseAssetAmount(
114
+ marketPosition: UserPosition
115
+ ): BN {
116
+ const allBids = marketPosition.baseAssetAmount.add(marketPosition.openBids);
117
+ const allAsks = marketPosition.baseAssetAmount.add(marketPosition.openAsks);
118
+
119
+ if (allBids.abs().gt(allAsks.abs())) {
120
+ return allBids;
121
+ } else {
122
+ return allAsks;
123
+ }
124
+ }
@@ -1,5 +1,11 @@
1
1
  import { BN } from '@project-serum/anchor';
2
- import { MarketAccount, PositionDirection } from '../types';
2
+ import {
3
+ MarketAccount,
4
+ PositionDirection,
5
+ MarginCategory,
6
+ BankAccount,
7
+ BankBalanceType,
8
+ } from '../types';
3
9
  import {
4
10
  calculateAmmReservesAfterSwap,
5
11
  calculatePrice,
@@ -7,7 +13,13 @@ import {
7
13
  getSwapDirection,
8
14
  calculateUpdatedAMM,
9
15
  } from './amm';
16
+ import {
17
+ calculateSizeDiscountAssetWeight,
18
+ calculateSizePremiumLiabilityWeight,
19
+ } from './margin';
10
20
  import { OraclePriceData } from '../oracles/types';
21
+ import { MARGIN_PRECISION } from '../constants/numericConstants';
22
+ import { getTokenAmount } from './bankBalance';
11
23
 
12
24
  /**
13
25
  * Calculates market mark price
@@ -103,3 +115,56 @@ export function calculateOracleSpread(
103
115
  ): BN {
104
116
  return price.sub(oraclePriceData.price);
105
117
  }
118
+
119
+ export function calculateMarketMarginRatio(
120
+ market: MarketAccount,
121
+ size: BN,
122
+ marginCategory: MarginCategory
123
+ ): number {
124
+ let marginRatio;
125
+ switch (marginCategory) {
126
+ case 'Initial':
127
+ marginRatio = calculateSizePremiumLiabilityWeight(
128
+ size,
129
+ market.imfFactor,
130
+ new BN(market.marginRatioInitial),
131
+ MARGIN_PRECISION
132
+ ).toNumber();
133
+ break;
134
+ case 'Maintenance':
135
+ marginRatio = market.marginRatioMaintenance;
136
+ break;
137
+ }
138
+
139
+ return marginRatio;
140
+ }
141
+
142
+ export function calculateUnsettledAssetWeight(
143
+ market: MarketAccount,
144
+ unsettledPnl: BN,
145
+ marginCategory: MarginCategory
146
+ ): BN {
147
+ let assetWeight: BN;
148
+
149
+ switch (marginCategory) {
150
+ case 'Initial':
151
+ assetWeight = calculateSizeDiscountAssetWeight(
152
+ unsettledPnl,
153
+ market.unsettledImfFactor,
154
+ new BN(market.unsettledInitialAssetWeight)
155
+ );
156
+ break;
157
+ case 'Maintenance':
158
+ assetWeight = new BN(market.unsettledMaintenanceAssetWeight);
159
+ break;
160
+ }
161
+
162
+ return assetWeight;
163
+ }
164
+
165
+ export function calculateMarketAvailablePNL(
166
+ market: MarketAccount,
167
+ bank: BankAccount
168
+ ): BN {
169
+ return getTokenAmount(market.pnlPool.balance, bank, BankBalanceType.DEPOSIT);
170
+ }
@@ -18,6 +18,8 @@ import {
18
18
  getSwapDirection,
19
19
  } from './amm';
20
20
 
21
+ import { calculateMarginBaseAssetValue } from './margin';
22
+
21
23
  /**
22
24
  * calculateBaseAssetValue
23
25
  * = market value of closing entire position
@@ -84,6 +86,7 @@ export function calculateBaseAssetValue(
84
86
  * @param market
85
87
  * @param marketPosition
86
88
  * @param withFunding (adds unrealized funding payment pnl to result)
89
+ * @param oraclePriceData
87
90
  * @returns BaseAssetAmount : Precision QUOTE_PRECISION
88
91
  */
89
92
  export function calculatePositionPNL(
@@ -93,21 +96,21 @@ export function calculatePositionPNL(
93
96
  oraclePriceData: OraclePriceData
94
97
  ): BN {
95
98
  if (marketPosition.baseAssetAmount.eq(ZERO)) {
96
- return ZERO;
99
+ return marketPosition.quoteAssetAmount;
97
100
  }
98
101
 
99
- const baseAssetValue = calculateBaseAssetValue(
102
+ const baseAssetValue = calculateMarginBaseAssetValue(
100
103
  market,
101
104
  marketPosition,
102
105
  oraclePriceData
103
106
  );
104
107
 
105
- let pnl;
106
- if (marketPosition.baseAssetAmount.gt(ZERO)) {
107
- pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount);
108
- } else {
109
- pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue);
110
- }
108
+ const baseAssetValueSign = marketPosition.baseAssetAmount.isNeg()
109
+ ? new BN(-1)
110
+ : new BN(1);
111
+ let pnl = baseAssetValue
112
+ .mul(baseAssetValueSign)
113
+ .add(marketPosition.quoteAssetAmount);
111
114
 
112
115
  if (withFunding) {
113
116
  const fundingRatePnL = calculatePositionFundingPNL(
@@ -121,6 +124,36 @@ export function calculatePositionPNL(
121
124
  return pnl;
122
125
  }
123
126
 
127
+ export function calculateUnsettledPnl(
128
+ market: MarketAccount,
129
+ marketPosition: UserPosition,
130
+ oraclePriceData: OraclePriceData
131
+ ): BN {
132
+ const unrealizedPnl = calculatePositionPNL(
133
+ market,
134
+ marketPosition,
135
+ true,
136
+ oraclePriceData
137
+ );
138
+
139
+ let unsettledPnl = unrealizedPnl;
140
+ if (unrealizedPnl.gt(ZERO)) {
141
+ const fundingPnL = calculatePositionFundingPNL(market, marketPosition).div(
142
+ PRICE_TO_QUOTE_PRECISION
143
+ );
144
+
145
+ const maxPositivePnl = BN.max(
146
+ marketPosition.quoteAssetAmount
147
+ .add(marketPosition.quoteEntryAmount)
148
+ .add(fundingPnL),
149
+ ZERO
150
+ );
151
+
152
+ unsettledPnl = BN.min(maxPositivePnl, unrealizedPnl);
153
+ }
154
+ return unsettledPnl;
155
+ }
156
+
124
157
  /**
125
158
  *
126
159
  * @param market
@@ -156,7 +189,7 @@ export function positionIsAvailable(position: UserPosition): boolean {
156
189
  return (
157
190
  position.baseAssetAmount.eq(ZERO) &&
158
191
  position.openOrders.eq(ZERO) &&
159
- position.unsettledPnl.eq(ZERO)
192
+ position.quoteAssetAmount.eq(ZERO)
160
193
  );
161
194
  }
162
195
 
@@ -170,6 +203,23 @@ export function calculateEntryPrice(userPosition: UserPosition): BN {
170
203
  return ZERO;
171
204
  }
172
205
 
206
+ return userPosition.quoteEntryAmount
207
+ .mul(MARK_PRICE_PRECISION)
208
+ .mul(AMM_TO_QUOTE_PRECISION_RATIO)
209
+ .div(userPosition.baseAssetAmount)
210
+ .abs();
211
+ }
212
+
213
+ /**
214
+ *
215
+ * @param userPosition
216
+ * @returns Precision: MARK_PRICE_PRECISION (10^10)
217
+ */
218
+ export function calculateCostBasis(userPosition: UserPosition): BN {
219
+ if (userPosition.baseAssetAmount.eq(ZERO)) {
220
+ return ZERO;
221
+ }
222
+
173
223
  return userPosition.quoteAssetAmount
174
224
  .mul(MARK_PRICE_PRECISION)
175
225
  .mul(AMM_TO_QUOTE_PRECISION_RATIO)