@drift-labs/sdk 2.31.1-beta.9 → 2.32.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/lib/constants/perpMarkets.js +20 -0
- package/lib/dlob/orderBookLevels.js +2 -2
- package/lib/driftClient.d.ts +51 -4
- package/lib/driftClient.js +195 -194
- package/lib/idl/drift.json +31 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/marinade/index.d.ts +11 -0
- package/lib/marinade/index.js +36 -0
- package/lib/marinade/types.d.ts +1963 -0
- package/lib/marinade/types.js +1965 -0
- package/lib/math/spotBalance.d.ts +9 -2
- package/lib/math/spotBalance.js +54 -6
- package/lib/math/superStake.d.ts +22 -0
- package/lib/math/superStake.js +108 -0
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +5 -1
- package/lib/orderParams.d.ts +18 -5
- package/lib/orderParams.js +17 -1
- package/lib/user.d.ts +45 -1
- package/lib/user.js +227 -9
- package/package.json +1 -1
- package/src/constants/perpMarkets.ts +20 -0
- package/src/dlob/orderBookLevels.ts +3 -2
- package/src/driftClient.ts +373 -223
- package/src/idl/drift.json +31 -1
- package/src/index.ts +3 -0
- package/src/marinade/idl/idl.json +1962 -0
- package/src/marinade/index.ts +64 -0
- package/src/marinade/types.ts +3925 -0
- package/src/math/spotBalance.ts +83 -5
- package/src/math/superStake.ts +148 -0
- package/src/math/utils.ts +4 -0
- package/src/orderParams.ts +35 -5
- package/src/user.ts +453 -15
- package/tests/spot/test.ts +156 -0
package/src/user.ts
CHANGED
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
OPEN_ORDER_MARGIN_REQUIREMENT,
|
|
29
29
|
FIVE_MINUTE,
|
|
30
30
|
BASE_PRECISION,
|
|
31
|
+
ONE,
|
|
32
|
+
TWO,
|
|
31
33
|
} from './constants/numericConstants';
|
|
32
34
|
import {
|
|
33
35
|
UserAccountSubscriber,
|
|
@@ -50,6 +52,8 @@ import {
|
|
|
50
52
|
calculateSpotMarketMarginRatio,
|
|
51
53
|
getSignedTokenAmount,
|
|
52
54
|
SpotBalanceType,
|
|
55
|
+
sigNum,
|
|
56
|
+
getBalance,
|
|
53
57
|
} from '.';
|
|
54
58
|
import {
|
|
55
59
|
getTokenAmount,
|
|
@@ -165,6 +169,18 @@ export class User {
|
|
|
165
169
|
);
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
getEmptySpotPosition(marketIndex: number): SpotPosition {
|
|
173
|
+
return {
|
|
174
|
+
marketIndex,
|
|
175
|
+
scaledBalance: ZERO,
|
|
176
|
+
balanceType: SpotBalanceType.DEPOSIT,
|
|
177
|
+
cumulativeDeposits: ZERO,
|
|
178
|
+
openAsks: ZERO,
|
|
179
|
+
openBids: ZERO,
|
|
180
|
+
openOrders: 0,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
168
184
|
/**
|
|
169
185
|
* Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
|
|
170
186
|
* Positive if it is a deposit, negative if it is a borrow.
|
|
@@ -442,7 +458,7 @@ export class User {
|
|
|
442
458
|
* @returns : Precision QUOTE_PRECISION
|
|
443
459
|
*/
|
|
444
460
|
public getFreeCollateral(): BN {
|
|
445
|
-
const totalCollateral = this.getTotalCollateral();
|
|
461
|
+
const totalCollateral = this.getTotalCollateral('Initial', true);
|
|
446
462
|
const initialMarginRequirement = this.getInitialMarginRequirement();
|
|
447
463
|
const freeCollateral = totalCollateral.sub(initialMarginRequirement);
|
|
448
464
|
return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
|
|
@@ -918,15 +934,6 @@ export class User {
|
|
|
918
934
|
return assetValue;
|
|
919
935
|
}
|
|
920
936
|
|
|
921
|
-
public getSpotTokenAmount(marketIndex: number): BN {
|
|
922
|
-
const spotPosition = this.getSpotPosition(marketIndex);
|
|
923
|
-
return getTokenAmount(
|
|
924
|
-
spotPosition.scaledBalance,
|
|
925
|
-
this.driftClient.getSpotMarketAccount(marketIndex),
|
|
926
|
-
spotPosition.balanceType
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
937
|
public getSpotPositionValue(
|
|
931
938
|
marketIndex: number,
|
|
932
939
|
marginCategory?: MarginCategory,
|
|
@@ -1231,10 +1238,20 @@ export class User {
|
|
|
1231
1238
|
* @returns : Precision TEN_THOUSAND
|
|
1232
1239
|
*/
|
|
1233
1240
|
public getLeverage(): BN {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
this.getLeverageComponents();
|
|
1241
|
+
return this.calculateLeverageFromComponents(this.getLeverageComponents());
|
|
1242
|
+
}
|
|
1237
1243
|
|
|
1244
|
+
calculateLeverageFromComponents({
|
|
1245
|
+
perpLiabilityValue,
|
|
1246
|
+
perpPnl,
|
|
1247
|
+
spotAssetValue,
|
|
1248
|
+
spotLiabilityValue,
|
|
1249
|
+
}: {
|
|
1250
|
+
perpLiabilityValue: BN;
|
|
1251
|
+
perpPnl: BN;
|
|
1252
|
+
spotAssetValue: BN;
|
|
1253
|
+
spotLiabilityValue: BN;
|
|
1254
|
+
}): BN {
|
|
1238
1255
|
const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
|
|
1239
1256
|
const totalAssetValue = spotAssetValue.add(perpPnl);
|
|
1240
1257
|
const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
|
|
@@ -1925,7 +1942,7 @@ export class User {
|
|
|
1925
1942
|
const marginRatio = calculateSpotMarketMarginRatio(
|
|
1926
1943
|
market,
|
|
1927
1944
|
'Initial',
|
|
1928
|
-
this.
|
|
1945
|
+
this.getTokenAmount(targetMarketIndex).abs(),
|
|
1929
1946
|
SpotBalanceType.BORROW
|
|
1930
1947
|
);
|
|
1931
1948
|
freeCollateral = freeCollateral.add(
|
|
@@ -1939,7 +1956,7 @@ export class User {
|
|
|
1939
1956
|
const marginRatio = calculateSpotMarketMarginRatio(
|
|
1940
1957
|
market,
|
|
1941
1958
|
'Initial',
|
|
1942
|
-
this.
|
|
1959
|
+
this.getTokenAmount(targetMarketIndex),
|
|
1943
1960
|
SpotBalanceType.DEPOSIT
|
|
1944
1961
|
);
|
|
1945
1962
|
freeCollateral = freeCollateral.add(
|
|
@@ -1962,6 +1979,427 @@ export class User {
|
|
|
1962
1979
|
return tradeAmount;
|
|
1963
1980
|
}
|
|
1964
1981
|
|
|
1982
|
+
/**
|
|
1983
|
+
* Calculates the max amount of token that can be swapped from inMarket to outMarket
|
|
1984
|
+
* Assumes swap happens at oracle price
|
|
1985
|
+
*
|
|
1986
|
+
* @param inMarketIndex
|
|
1987
|
+
* @param outMarketIndex
|
|
1988
|
+
* @param calculateSwap function to similate in to out swa
|
|
1989
|
+
* @param iterationLimit how long to run appromixation before erroring out
|
|
1990
|
+
*/
|
|
1991
|
+
public getMaxSwapAmount({
|
|
1992
|
+
inMarketIndex,
|
|
1993
|
+
outMarketIndex,
|
|
1994
|
+
calculateSwap,
|
|
1995
|
+
iterationLimit = 1000,
|
|
1996
|
+
}: {
|
|
1997
|
+
inMarketIndex: number;
|
|
1998
|
+
outMarketIndex: number;
|
|
1999
|
+
calculateSwap?: (inAmount: BN) => BN;
|
|
2000
|
+
iterationLimit?: number;
|
|
2001
|
+
}): { inAmount: BN; outAmount: BN; leverage: BN } {
|
|
2002
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
2003
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
2004
|
+
|
|
2005
|
+
const inOraclePrice = this.getOracleDataForSpotMarket(inMarketIndex).price;
|
|
2006
|
+
const outOraclePrice =
|
|
2007
|
+
this.getOracleDataForSpotMarket(outMarketIndex).price;
|
|
2008
|
+
|
|
2009
|
+
const inPrecision = new BN(10 ** inMarket.decimals);
|
|
2010
|
+
const outPrecision = new BN(10 ** outMarket.decimals);
|
|
2011
|
+
|
|
2012
|
+
const outSaferThanIn =
|
|
2013
|
+
inMarket.initialAssetWeight < outMarket.initialAssetWeight;
|
|
2014
|
+
|
|
2015
|
+
const inSpotPosition =
|
|
2016
|
+
this.getSpotPosition(inMarketIndex) ||
|
|
2017
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
2018
|
+
const outSpotPosition =
|
|
2019
|
+
this.getSpotPosition(outMarketIndex) ||
|
|
2020
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
2021
|
+
|
|
2022
|
+
const freeCollateral = this.getFreeCollateral();
|
|
2023
|
+
|
|
2024
|
+
const inContributionInitial =
|
|
2025
|
+
this.calculateSpotPositionFreeCollateralContribution(inSpotPosition);
|
|
2026
|
+
const {
|
|
2027
|
+
totalAssetValue: inTotalAssetValueInitial,
|
|
2028
|
+
totalLiabilityValue: inTotalLiabilityValueInitial,
|
|
2029
|
+
} = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
2030
|
+
const outContributionInitial =
|
|
2031
|
+
this.calculateSpotPositionFreeCollateralContribution(outSpotPosition);
|
|
2032
|
+
const {
|
|
2033
|
+
totalAssetValue: outTotalAssetValueInitial,
|
|
2034
|
+
totalLiabilityValue: outTotalLiabilityValueInitial,
|
|
2035
|
+
} = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
2036
|
+
const initialContribution = inContributionInitial.add(
|
|
2037
|
+
outContributionInitial
|
|
2038
|
+
);
|
|
2039
|
+
|
|
2040
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2041
|
+
this.getLeverageComponents();
|
|
2042
|
+
|
|
2043
|
+
if (!calculateSwap) {
|
|
2044
|
+
calculateSwap = (inSwap: BN) => {
|
|
2045
|
+
return inSwap
|
|
2046
|
+
.mul(outPrecision)
|
|
2047
|
+
.mul(inOraclePrice)
|
|
2048
|
+
.div(outOraclePrice)
|
|
2049
|
+
.div(inPrecision);
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
let inSwap = ZERO;
|
|
2054
|
+
let outSwap = ZERO;
|
|
2055
|
+
const inTokenAmount = this.getTokenAmount(inMarketIndex);
|
|
2056
|
+
if (freeCollateral.lt(ONE)) {
|
|
2057
|
+
if (outSaferThanIn && inTokenAmount.gt(ZERO)) {
|
|
2058
|
+
inSwap = inTokenAmount;
|
|
2059
|
+
outSwap = calculateSwap(inSwap);
|
|
2060
|
+
}
|
|
2061
|
+
} else {
|
|
2062
|
+
let minSwap = ZERO;
|
|
2063
|
+
let maxSwap = freeCollateral
|
|
2064
|
+
.mul(inPrecision)
|
|
2065
|
+
.mul(SPOT_MARKET_WEIGHT_PRECISION)
|
|
2066
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION.div(new BN(100)))
|
|
2067
|
+
.div(inOraclePrice); // just assume user can go 100x
|
|
2068
|
+
inSwap = maxSwap.div(TWO);
|
|
2069
|
+
const error = BN.min(QUOTE_PRECISION, freeCollateral.div(new BN(100)));
|
|
2070
|
+
|
|
2071
|
+
let i = 0;
|
|
2072
|
+
let freeCollateralAfter = freeCollateral;
|
|
2073
|
+
while (freeCollateralAfter.gt(error) || freeCollateralAfter.isNeg()) {
|
|
2074
|
+
outSwap = calculateSwap(inSwap);
|
|
2075
|
+
|
|
2076
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2077
|
+
inSpotPosition,
|
|
2078
|
+
inSwap.neg(),
|
|
2079
|
+
inMarket
|
|
2080
|
+
);
|
|
2081
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2082
|
+
outSpotPosition,
|
|
2083
|
+
outSwap,
|
|
2084
|
+
outMarket
|
|
2085
|
+
);
|
|
2086
|
+
|
|
2087
|
+
const inContributionAfter =
|
|
2088
|
+
this.calculateSpotPositionFreeCollateralContribution(inPositionAfter);
|
|
2089
|
+
const outContributionAfter =
|
|
2090
|
+
this.calculateSpotPositionFreeCollateralContribution(
|
|
2091
|
+
outPositionAfter
|
|
2092
|
+
);
|
|
2093
|
+
|
|
2094
|
+
const contributionAfter = inContributionAfter.add(outContributionAfter);
|
|
2095
|
+
|
|
2096
|
+
const contributionDelta = contributionAfter.sub(initialContribution);
|
|
2097
|
+
|
|
2098
|
+
freeCollateralAfter = freeCollateral.add(contributionDelta);
|
|
2099
|
+
|
|
2100
|
+
if (freeCollateralAfter.gt(error)) {
|
|
2101
|
+
minSwap = inSwap;
|
|
2102
|
+
inSwap = minSwap.add(maxSwap).div(TWO);
|
|
2103
|
+
} else if (freeCollateralAfter.isNeg()) {
|
|
2104
|
+
maxSwap = inSwap;
|
|
2105
|
+
inSwap = minSwap.add(maxSwap).div(TWO);
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
if (i++ > iterationLimit) {
|
|
2109
|
+
throw new Error('getMaxSwapAmount iteration limit reached');
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2115
|
+
inSpotPosition,
|
|
2116
|
+
inSwap.neg(),
|
|
2117
|
+
inMarket
|
|
2118
|
+
);
|
|
2119
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2120
|
+
outSpotPosition,
|
|
2121
|
+
outSwap,
|
|
2122
|
+
outMarket
|
|
2123
|
+
);
|
|
2124
|
+
|
|
2125
|
+
const {
|
|
2126
|
+
totalAssetValue: inTotalAssetValueAfter,
|
|
2127
|
+
totalLiabilityValue: inTotalLiabilityValueAfter,
|
|
2128
|
+
} = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
2129
|
+
|
|
2130
|
+
const {
|
|
2131
|
+
totalAssetValue: outTotalAssetValueAfter,
|
|
2132
|
+
totalLiabilityValue: outTotalLiabilityValueAfter,
|
|
2133
|
+
} = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
2134
|
+
|
|
2135
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
2136
|
+
.add(outTotalAssetValueAfter)
|
|
2137
|
+
.sub(inTotalAssetValueInitial)
|
|
2138
|
+
.sub(outTotalAssetValueInitial);
|
|
2139
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
2140
|
+
.add(outTotalLiabilityValueAfter)
|
|
2141
|
+
.sub(inTotalLiabilityValueInitial)
|
|
2142
|
+
.sub(outTotalLiabilityValueInitial);
|
|
2143
|
+
|
|
2144
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
2145
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(
|
|
2146
|
+
spotLiabilityValueDelta
|
|
2147
|
+
);
|
|
2148
|
+
|
|
2149
|
+
const leverage = this.calculateLeverageFromComponents({
|
|
2150
|
+
perpLiabilityValue,
|
|
2151
|
+
perpPnl,
|
|
2152
|
+
spotAssetValue: spotAssetValueAfter,
|
|
2153
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
return { inAmount: inSwap, outAmount: outSwap, leverage };
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
public cloneAndUpdateSpotPosition(
|
|
2160
|
+
position: SpotPosition,
|
|
2161
|
+
tokenAmount: BN,
|
|
2162
|
+
market: SpotMarketAccount
|
|
2163
|
+
): SpotPosition {
|
|
2164
|
+
const clonedPosition = Object.assign({}, position);
|
|
2165
|
+
if (tokenAmount.eq(ZERO)) {
|
|
2166
|
+
return clonedPosition;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
const preTokenAmount = getSignedTokenAmount(
|
|
2170
|
+
getTokenAmount(position.scaledBalance, market, position.balanceType),
|
|
2171
|
+
position.balanceType
|
|
2172
|
+
);
|
|
2173
|
+
|
|
2174
|
+
if (sigNum(preTokenAmount).eq(sigNum(tokenAmount))) {
|
|
2175
|
+
const scaledBalanceDelta = getBalance(
|
|
2176
|
+
tokenAmount.abs(),
|
|
2177
|
+
market,
|
|
2178
|
+
position.balanceType
|
|
2179
|
+
);
|
|
2180
|
+
clonedPosition.scaledBalance =
|
|
2181
|
+
clonedPosition.scaledBalance.add(scaledBalanceDelta);
|
|
2182
|
+
return clonedPosition;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
const updateDirection = tokenAmount.isNeg()
|
|
2186
|
+
? SpotBalanceType.BORROW
|
|
2187
|
+
: SpotBalanceType.DEPOSIT;
|
|
2188
|
+
|
|
2189
|
+
if (tokenAmount.abs().gte(preTokenAmount.abs())) {
|
|
2190
|
+
clonedPosition.scaledBalance = getBalance(
|
|
2191
|
+
tokenAmount.abs().sub(preTokenAmount.abs()),
|
|
2192
|
+
market,
|
|
2193
|
+
updateDirection
|
|
2194
|
+
);
|
|
2195
|
+
clonedPosition.balanceType = updateDirection;
|
|
2196
|
+
} else {
|
|
2197
|
+
const scaledBalanceDelta = getBalance(
|
|
2198
|
+
tokenAmount.abs(),
|
|
2199
|
+
market,
|
|
2200
|
+
position.balanceType
|
|
2201
|
+
);
|
|
2202
|
+
|
|
2203
|
+
clonedPosition.scaledBalance =
|
|
2204
|
+
clonedPosition.scaledBalance.sub(scaledBalanceDelta);
|
|
2205
|
+
}
|
|
2206
|
+
return clonedPosition;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
calculateSpotPositionFreeCollateralContribution(
|
|
2210
|
+
spotPosition: SpotPosition
|
|
2211
|
+
): BN {
|
|
2212
|
+
let freeCollateralContribution = ZERO;
|
|
2213
|
+
const now = new BN(new Date().getTime() / 1000);
|
|
2214
|
+
const strict = true;
|
|
2215
|
+
const marginCategory = 'Initial';
|
|
2216
|
+
|
|
2217
|
+
const spotMarketAccount: SpotMarketAccount =
|
|
2218
|
+
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
2219
|
+
|
|
2220
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
2221
|
+
spotPosition.marketIndex
|
|
2222
|
+
);
|
|
2223
|
+
|
|
2224
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
|
|
2225
|
+
getWorstCaseTokenAmounts(
|
|
2226
|
+
spotPosition,
|
|
2227
|
+
spotMarketAccount,
|
|
2228
|
+
oraclePriceData
|
|
2229
|
+
);
|
|
2230
|
+
|
|
2231
|
+
if (worstCaseTokenAmount.gt(ZERO)) {
|
|
2232
|
+
const baseAssetValue = this.getSpotAssetValue(
|
|
2233
|
+
worstCaseTokenAmount,
|
|
2234
|
+
oraclePriceData,
|
|
2235
|
+
spotMarketAccount,
|
|
2236
|
+
marginCategory,
|
|
2237
|
+
strict,
|
|
2238
|
+
now
|
|
2239
|
+
);
|
|
2240
|
+
|
|
2241
|
+
freeCollateralContribution =
|
|
2242
|
+
freeCollateralContribution.add(baseAssetValue);
|
|
2243
|
+
} else {
|
|
2244
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(
|
|
2245
|
+
worstCaseTokenAmount,
|
|
2246
|
+
oraclePriceData,
|
|
2247
|
+
spotMarketAccount,
|
|
2248
|
+
marginCategory,
|
|
2249
|
+
undefined,
|
|
2250
|
+
strict,
|
|
2251
|
+
now
|
|
2252
|
+
).abs();
|
|
2253
|
+
|
|
2254
|
+
freeCollateralContribution =
|
|
2255
|
+
freeCollateralContribution.sub(baseLiabilityValue);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
freeCollateralContribution.add(worstCaseQuoteTokenAmount);
|
|
2259
|
+
|
|
2260
|
+
return freeCollateralContribution;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
calculateSpotPositionLeverageContribution(spotPosition: SpotPosition): {
|
|
2264
|
+
totalAssetValue: BN;
|
|
2265
|
+
totalLiabilityValue: BN;
|
|
2266
|
+
} {
|
|
2267
|
+
let totalAssetValue = ZERO;
|
|
2268
|
+
let totalLiabilityValue = ZERO;
|
|
2269
|
+
const now = new BN(new Date().getTime() / 1000);
|
|
2270
|
+
|
|
2271
|
+
const spotMarketAccount: SpotMarketAccount =
|
|
2272
|
+
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
2273
|
+
|
|
2274
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
2275
|
+
spotPosition.marketIndex
|
|
2276
|
+
);
|
|
2277
|
+
|
|
2278
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
|
|
2279
|
+
getWorstCaseTokenAmounts(
|
|
2280
|
+
spotPosition,
|
|
2281
|
+
spotMarketAccount,
|
|
2282
|
+
oraclePriceData
|
|
2283
|
+
);
|
|
2284
|
+
|
|
2285
|
+
if (worstCaseTokenAmount.gt(ZERO)) {
|
|
2286
|
+
totalAssetValue = this.getSpotAssetValue(
|
|
2287
|
+
worstCaseTokenAmount,
|
|
2288
|
+
oraclePriceData,
|
|
2289
|
+
spotMarketAccount,
|
|
2290
|
+
undefined,
|
|
2291
|
+
false,
|
|
2292
|
+
now
|
|
2293
|
+
);
|
|
2294
|
+
} else {
|
|
2295
|
+
totalLiabilityValue = this.getSpotLiabilityValue(
|
|
2296
|
+
worstCaseTokenAmount,
|
|
2297
|
+
oraclePriceData,
|
|
2298
|
+
spotMarketAccount,
|
|
2299
|
+
undefined,
|
|
2300
|
+
undefined,
|
|
2301
|
+
false,
|
|
2302
|
+
now
|
|
2303
|
+
).abs();
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
if (worstCaseQuoteTokenAmount.gt(ZERO)) {
|
|
2307
|
+
totalAssetValue = totalAssetValue.add(worstCaseQuoteTokenAmount);
|
|
2308
|
+
} else {
|
|
2309
|
+
totalLiabilityValue = totalLiabilityValue.add(
|
|
2310
|
+
worstCaseQuoteTokenAmount.abs()
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
return {
|
|
2315
|
+
totalAssetValue,
|
|
2316
|
+
totalLiabilityValue,
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
/**
|
|
2321
|
+
* Estimates what the user leverage will be after swap
|
|
2322
|
+
* @param inMarketIndex
|
|
2323
|
+
* @param outMarketIndex
|
|
2324
|
+
* @param inAmount
|
|
2325
|
+
* @param outAmount
|
|
2326
|
+
*/
|
|
2327
|
+
public accountLeverageAfterSwap({
|
|
2328
|
+
inMarketIndex,
|
|
2329
|
+
outMarketIndex,
|
|
2330
|
+
inAmount,
|
|
2331
|
+
outAmount,
|
|
2332
|
+
}: {
|
|
2333
|
+
inMarketIndex: number;
|
|
2334
|
+
outMarketIndex: number;
|
|
2335
|
+
inAmount: BN;
|
|
2336
|
+
outAmount: BN;
|
|
2337
|
+
}): BN {
|
|
2338
|
+
const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
|
|
2339
|
+
const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
|
|
2340
|
+
|
|
2341
|
+
const inSpotPosition =
|
|
2342
|
+
this.getSpotPosition(inMarketIndex) ||
|
|
2343
|
+
this.getEmptySpotPosition(inMarketIndex);
|
|
2344
|
+
const outSpotPosition =
|
|
2345
|
+
this.getSpotPosition(outMarketIndex) ||
|
|
2346
|
+
this.getEmptySpotPosition(outMarketIndex);
|
|
2347
|
+
|
|
2348
|
+
const {
|
|
2349
|
+
totalAssetValue: inTotalAssetValueInitial,
|
|
2350
|
+
totalLiabilityValue: inTotalLiabilityValueInitial,
|
|
2351
|
+
} = this.calculateSpotPositionLeverageContribution(inSpotPosition);
|
|
2352
|
+
const {
|
|
2353
|
+
totalAssetValue: outTotalAssetValueInitial,
|
|
2354
|
+
totalLiabilityValue: outTotalLiabilityValueInitial,
|
|
2355
|
+
} = this.calculateSpotPositionLeverageContribution(outSpotPosition);
|
|
2356
|
+
|
|
2357
|
+
const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
|
|
2358
|
+
this.getLeverageComponents();
|
|
2359
|
+
|
|
2360
|
+
const inPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2361
|
+
inSpotPosition,
|
|
2362
|
+
inAmount.abs().neg(),
|
|
2363
|
+
inMarket
|
|
2364
|
+
);
|
|
2365
|
+
const outPositionAfter = this.cloneAndUpdateSpotPosition(
|
|
2366
|
+
outSpotPosition,
|
|
2367
|
+
outAmount.abs(),
|
|
2368
|
+
outMarket
|
|
2369
|
+
);
|
|
2370
|
+
|
|
2371
|
+
const {
|
|
2372
|
+
totalAssetValue: inTotalAssetValueAfter,
|
|
2373
|
+
totalLiabilityValue: inTotalLiabilityValueAfter,
|
|
2374
|
+
} = this.calculateSpotPositionLeverageContribution(inPositionAfter);
|
|
2375
|
+
|
|
2376
|
+
const {
|
|
2377
|
+
totalAssetValue: outTotalAssetValueAfter,
|
|
2378
|
+
totalLiabilityValue: outTotalLiabilityValueAfter,
|
|
2379
|
+
} = this.calculateSpotPositionLeverageContribution(outPositionAfter);
|
|
2380
|
+
|
|
2381
|
+
const spotAssetValueDelta = inTotalAssetValueAfter
|
|
2382
|
+
.add(outTotalAssetValueAfter)
|
|
2383
|
+
.sub(inTotalAssetValueInitial)
|
|
2384
|
+
.sub(outTotalAssetValueInitial);
|
|
2385
|
+
const spotLiabilityValueDelta = inTotalLiabilityValueAfter
|
|
2386
|
+
.add(outTotalLiabilityValueAfter)
|
|
2387
|
+
.sub(inTotalLiabilityValueInitial)
|
|
2388
|
+
.sub(outTotalLiabilityValueInitial);
|
|
2389
|
+
|
|
2390
|
+
const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
|
|
2391
|
+
const spotLiabilityValueAfter = spotLiabilityValue.add(
|
|
2392
|
+
spotLiabilityValueDelta
|
|
2393
|
+
);
|
|
2394
|
+
|
|
2395
|
+
return this.calculateLeverageFromComponents({
|
|
2396
|
+
perpLiabilityValue,
|
|
2397
|
+
perpPnl,
|
|
2398
|
+
spotAssetValue: spotAssetValueAfter,
|
|
2399
|
+
spotLiabilityValue: spotLiabilityValueAfter,
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
|
|
1965
2403
|
// TODO - should this take the price impact of the trade into account for strict accuracy?
|
|
1966
2404
|
|
|
1967
2405
|
/**
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BN,
|
|
3
|
+
ZERO,
|
|
4
|
+
calculateSpotMarketBorrowCapacity,
|
|
5
|
+
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
|
|
6
|
+
} from '../../src';
|
|
7
|
+
import { mockSpotMarkets } from '../dlob/helpers';
|
|
8
|
+
|
|
9
|
+
import { assert } from '../../src/assert/assert';
|
|
10
|
+
|
|
11
|
+
describe('Spot Tests', () => {
|
|
12
|
+
it('base borrow capacity', () => {
|
|
13
|
+
const mockSpot = mockSpotMarkets[0];
|
|
14
|
+
mockSpot.maxBorrowRate = 1000000;
|
|
15
|
+
mockSpot.optimalBorrowRate = 100000;
|
|
16
|
+
mockSpot.optimalUtilization = 700000;
|
|
17
|
+
|
|
18
|
+
mockSpot.decimals = 9;
|
|
19
|
+
mockSpot.cumulativeDepositInterest =
|
|
20
|
+
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION;
|
|
21
|
+
mockSpot.cumulativeBorrowInterest =
|
|
22
|
+
SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION;
|
|
23
|
+
|
|
24
|
+
const tokenAmount = 100000;
|
|
25
|
+
// no borrows
|
|
26
|
+
mockSpot.depositBalance = new BN(tokenAmount * 1e9);
|
|
27
|
+
mockSpot.borrowBalance = ZERO;
|
|
28
|
+
|
|
29
|
+
// todo, should incorp all other spot market constraints?
|
|
30
|
+
const aboveMaxAmount = calculateSpotMarketBorrowCapacity(
|
|
31
|
+
mockSpot,
|
|
32
|
+
new BN(2000000)
|
|
33
|
+
);
|
|
34
|
+
assert(aboveMaxAmount.gt(mockSpot.depositBalance));
|
|
35
|
+
|
|
36
|
+
const maxAmount = calculateSpotMarketBorrowCapacity(
|
|
37
|
+
mockSpot,
|
|
38
|
+
new BN(1000000)
|
|
39
|
+
);
|
|
40
|
+
assert(maxAmount.eq(mockSpot.depositBalance));
|
|
41
|
+
|
|
42
|
+
const optAmount = calculateSpotMarketBorrowCapacity(
|
|
43
|
+
mockSpot,
|
|
44
|
+
new BN(100000)
|
|
45
|
+
);
|
|
46
|
+
const ans = new BN((mockSpot.depositBalance.toNumber() * 7) / 10);
|
|
47
|
+
// console.log('optAmount:', optAmount.toNumber(), ans.toNumber());
|
|
48
|
+
assert(optAmount.eq(ans));
|
|
49
|
+
|
|
50
|
+
const betweenOptMaxAmount = calculateSpotMarketBorrowCapacity(
|
|
51
|
+
mockSpot,
|
|
52
|
+
new BN(810000)
|
|
53
|
+
);
|
|
54
|
+
// console.log('betweenOptMaxAmount:', betweenOptMaxAmount.toNumber());
|
|
55
|
+
assert(betweenOptMaxAmount.lt(mockSpot.depositBalance));
|
|
56
|
+
assert(betweenOptMaxAmount.gt(ans));
|
|
57
|
+
assert(betweenOptMaxAmount.eq(new BN(93666600000000)));
|
|
58
|
+
|
|
59
|
+
const belowOptAmount = calculateSpotMarketBorrowCapacity(
|
|
60
|
+
mockSpot,
|
|
61
|
+
new BN(50000)
|
|
62
|
+
);
|
|
63
|
+
// console.log('belowOptAmount:', belowOptAmount.toNumber());
|
|
64
|
+
assert(belowOptAmount.eq(ans.div(new BN(2))));
|
|
65
|
+
|
|
66
|
+
const belowOptAmount2 = calculateSpotMarketBorrowCapacity(
|
|
67
|
+
mockSpot,
|
|
68
|
+
new BN(24900)
|
|
69
|
+
);
|
|
70
|
+
// console.log('belowOptAmount2:', belowOptAmount2.toNumber());
|
|
71
|
+
assert(belowOptAmount2.lt(ans.div(new BN(4))));
|
|
72
|
+
assert(belowOptAmount2.eq(new BN('17430000000000')));
|
|
73
|
+
|
|
74
|
+
const belowOptAmount3 = calculateSpotMarketBorrowCapacity(
|
|
75
|
+
mockSpot,
|
|
76
|
+
new BN(1)
|
|
77
|
+
);
|
|
78
|
+
// console.log('belowOptAmount3:', belowOptAmount3.toNumber());
|
|
79
|
+
assert(belowOptAmount3.eq(new BN('700000000'))); //0.7
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('complex borrow capacity', () => {
|
|
83
|
+
const mockSpot = mockSpotMarkets[0];
|
|
84
|
+
mockSpot.maxBorrowRate = 1000000;
|
|
85
|
+
mockSpot.optimalBorrowRate = 70000;
|
|
86
|
+
mockSpot.optimalUtilization = 700000;
|
|
87
|
+
|
|
88
|
+
mockSpot.decimals = 9;
|
|
89
|
+
mockSpot.cumulativeDepositInterest = new BN(
|
|
90
|
+
1.0154217042 * SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION.toNumber()
|
|
91
|
+
);
|
|
92
|
+
mockSpot.cumulativeBorrowInterest = new BN(
|
|
93
|
+
1.0417153549 * SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION.toNumber()
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
mockSpot.depositBalance = new BN(88522.734106451 * 1e9);
|
|
97
|
+
mockSpot.borrowBalance = new BN(7089.91675884 * 1e9);
|
|
98
|
+
|
|
99
|
+
// todo, should incorp all other spot market constraints?
|
|
100
|
+
const aboveMaxAmount = calculateSpotMarketBorrowCapacity(
|
|
101
|
+
mockSpot,
|
|
102
|
+
new BN(2000000)
|
|
103
|
+
);
|
|
104
|
+
assert(aboveMaxAmount.eq(new BN('111498270939007')));
|
|
105
|
+
|
|
106
|
+
const maxAmount = calculateSpotMarketBorrowCapacity(
|
|
107
|
+
mockSpot,
|
|
108
|
+
new BN(1000000)
|
|
109
|
+
);
|
|
110
|
+
assert(maxAmount.eq(new BN('82502230374168')));
|
|
111
|
+
// console.log('aboveMaxAmount:', aboveMaxAmount.toNumber(), 'maxAmount:', maxAmount.toNumber());
|
|
112
|
+
const optAmount = calculateSpotMarketBorrowCapacity(
|
|
113
|
+
mockSpot,
|
|
114
|
+
new BN(70000)
|
|
115
|
+
);
|
|
116
|
+
// console.log('optAmount:', optAmount.toNumber());
|
|
117
|
+
assert(optAmount.eq(new BN('55535858716123'))); // ~ 55535
|
|
118
|
+
|
|
119
|
+
const betweenOptMaxAmount = calculateSpotMarketBorrowCapacity(
|
|
120
|
+
mockSpot,
|
|
121
|
+
new BN(810000)
|
|
122
|
+
);
|
|
123
|
+
// console.log('betweenOptMaxAmount:', betweenOptMaxAmount.toNumber());
|
|
124
|
+
assert(betweenOptMaxAmount.lt(maxAmount));
|
|
125
|
+
assert(betweenOptMaxAmount.eq(new BN(76992910756523)));
|
|
126
|
+
assert(betweenOptMaxAmount.gt(optAmount));
|
|
127
|
+
|
|
128
|
+
const belowOptAmount = calculateSpotMarketBorrowCapacity(
|
|
129
|
+
mockSpot,
|
|
130
|
+
new BN(50000)
|
|
131
|
+
);
|
|
132
|
+
// console.log('belowOptAmount:', belowOptAmount.toNumber());
|
|
133
|
+
assert(belowOptAmount.eq(new BN('37558277610760')));
|
|
134
|
+
|
|
135
|
+
const belowOptAmount2 = calculateSpotMarketBorrowCapacity(
|
|
136
|
+
mockSpot,
|
|
137
|
+
new BN(24900)
|
|
138
|
+
);
|
|
139
|
+
// console.log('belowOptAmount2:', belowOptAmount2.toNumber());
|
|
140
|
+
assert(belowOptAmount2.eq(new BN('14996413323529')));
|
|
141
|
+
|
|
142
|
+
const belowOptAmount3 = calculateSpotMarketBorrowCapacity(
|
|
143
|
+
mockSpot,
|
|
144
|
+
new BN(4900)
|
|
145
|
+
);
|
|
146
|
+
// console.log('belowOptAmount2:', belowOptAmount3.toNumber());
|
|
147
|
+
assert(belowOptAmount3.eq(new BN('0')));
|
|
148
|
+
|
|
149
|
+
const belowOptAmount4 = calculateSpotMarketBorrowCapacity(
|
|
150
|
+
mockSpot,
|
|
151
|
+
new BN(1)
|
|
152
|
+
);
|
|
153
|
+
// console.log('belowOptAmount3:', belowOptAmount4.toNumber());
|
|
154
|
+
assert(belowOptAmount4.eq(new BN('0')));
|
|
155
|
+
});
|
|
156
|
+
});
|