@velocity-exchange/sdk 0.4.0 → 0.6.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/README.md +3 -3
- package/lib/browser/adminClient.d.ts +35 -0
- package/lib/browser/adminClient.js +65 -0
- package/lib/browser/config.js +6 -7
- package/lib/browser/constants/numericConstants.d.ts +1 -1
- package/lib/browser/constants/numericConstants.js +1 -1
- package/lib/browser/decode/user.js +5 -1
- package/lib/browser/idl/velocity.d.ts +146 -3
- package/lib/browser/idl/velocity.json +147 -4
- package/lib/browser/orderSubscriber/OrderSubscriber.js +3 -0
- package/lib/browser/pyth/pythLazerSubscriber.js +37 -27
- package/lib/browser/types.d.ts +4 -0
- package/lib/browser/user.d.ts +15 -0
- package/lib/browser/user.js +37 -1
- package/lib/browser/velocityClient.d.ts +46 -10
- package/lib/browser/velocityClient.js +107 -35
- package/lib/node/adminClient.d.ts +35 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +65 -0
- package/lib/node/config.d.ts.map +1 -1
- package/lib/node/config.js +6 -7
- package/lib/node/constants/numericConstants.d.ts +1 -1
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +1 -1
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +5 -1
- package/lib/node/idl/velocity.d.ts +146 -3
- package/lib/node/idl/velocity.d.ts.map +1 -1
- package/lib/node/idl/velocity.json +147 -4
- package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
- package/lib/node/orderSubscriber/OrderSubscriber.js +3 -0
- package/lib/node/pyth/pythLazerSubscriber.d.ts.map +1 -1
- package/lib/node/pyth/pythLazerSubscriber.js +37 -27
- package/lib/node/types.d.ts +4 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/user.d.ts +15 -0
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +37 -1
- package/lib/node/velocityClient.d.ts +46 -10
- package/lib/node/velocityClient.d.ts.map +1 -1
- package/lib/node/velocityClient.js +107 -35
- package/package.json +7 -3
- package/.prettierignore +0 -1
- package/CHANGELOG.md +0 -132
- package/build-browser.js +0 -58
- package/bunfig.toml +0 -3
- package/esbuild-shims.js +0 -12
- package/scripts/deposit-isolated-positions.ts +0 -110
- package/scripts/find-flagged-users.ts +0 -213
- package/scripts/grpc-client-test-comparison.ts +0 -372
- package/scripts/grpc-multiuser-client-test-comparison.ts +0 -158
- package/scripts/postbuild.js +0 -95
- package/scripts/single-grpc-client-test.ts +0 -284
- package/scripts/withdraw-isolated-positions.ts +0 -174
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +0 -95
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +0 -135
- package/src/accounts/basicUserAccountSubscriber.ts +0 -79
- package/src/accounts/basicUserStatsAccountSubscriber.ts +0 -87
- package/src/accounts/bulkAccountLoader.ts +0 -358
- package/src/accounts/bulkUserStatsSubscription.ts +0 -40
- package/src/accounts/bulkUserSubscription.ts +0 -39
- package/src/accounts/customizedCadenceBulkAccountLoader.ts +0 -250
- package/src/accounts/fetch.ts +0 -200
- package/src/accounts/grpcAccountSubscriber.ts +0 -225
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -73
- package/src/accounts/grpcMultiAccountSubscriber.ts +0 -566
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +0 -328
- package/src/accounts/grpcProgramAccountSubscriber.ts +0 -264
- package/src/accounts/grpcUserAccountSubscriber.ts +0 -63
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -65
- package/src/accounts/grpcVelocityClientAccountSubscriber.ts +0 -257
- package/src/accounts/grpcVelocityClientAccountSubscriberV2.ts +0 -941
- package/src/accounts/laserProgramAccountSubscriber.ts +0 -266
- package/src/accounts/oneShotUserAccountSubscriber.ts +0 -88
- package/src/accounts/oneShotUserStatsAccountSubscriber.ts +0 -94
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -223
- package/src/accounts/pollingOracleAccountSubscriber.ts +0 -151
- package/src/accounts/pollingTokenAccountSubscriber.ts +0 -147
- package/src/accounts/pollingUserAccountSubscriber.ts +0 -198
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -180
- package/src/accounts/pollingVelocityClientAccountSubscriber.ts +0 -774
- package/src/accounts/testBulkAccountLoader.ts +0 -59
- package/src/accounts/types.ts +0 -428
- package/src/accounts/utils.ts +0 -90
- package/src/accounts/webSocketAccountSubscriber.ts +0 -304
- package/src/accounts/webSocketAccountSubscriberV2.ts +0 -536
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +0 -171
- package/src/accounts/webSocketProgramAccountSubscriber.ts +0 -244
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -1048
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +0 -1042
- package/src/accounts/webSocketUserAccountSubscriber.ts +0 -139
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +0 -131
- package/src/accounts/webSocketVelocityClientAccountSubscriber.ts +0 -865
- package/src/accounts/webSocketVelocityClientAccountSubscriberV2.ts +0 -897
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +0 -127
- package/src/addresses/marketAddresses.ts +0 -28
- package/src/addresses/pda.ts +0 -620
- package/src/adminClient.ts +0 -8136
- package/src/assert/assert.ts +0 -5
- package/src/auctionSubscriber/auctionSubscriber.ts +0 -86
- package/src/auctionSubscriber/auctionSubscriberGrpc.ts +0 -99
- package/src/auctionSubscriber/index.ts +0 -9
- package/src/auctionSubscriber/types.ts +0 -30
- package/src/bankrun/bankrunConnection.ts +0 -630
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +0 -160
- package/src/blockhashSubscriber/index.ts +0 -6
- package/src/blockhashSubscriber/types.ts +0 -13
- package/src/clock/clockSubscriber.ts +0 -146
- package/src/config.ts +0 -312
- package/src/constants/index.ts +0 -9
- package/src/constants/numericConstants.ts +0 -194
- package/src/constants/perpMarkets.ts +0 -127
- package/src/constants/spotMarkets.ts +0 -104
- package/src/constants/txConstants.ts +0 -2
- package/src/constituentMap/constituentMap.ts +0 -305
- package/src/constituentMap/pollingConstituentAccountSubscriber.ts +0 -99
- package/src/constituentMap/webSocketConstituentAccountSubscriber.ts +0 -121
- package/src/core/VelocityCore.ts +0 -606
- package/src/core/index.ts +0 -12
- package/src/core/instructions/deposit.ts +0 -64
- package/src/core/instructions/fill.ts +0 -51
- package/src/core/instructions/funding.ts +0 -33
- package/src/core/instructions/liquidation.ts +0 -55
- package/src/core/instructions/orders.ts +0 -84
- package/src/core/instructions/perpOrders.ts +0 -312
- package/src/core/instructions/settlement.ts +0 -40
- package/src/core/instructions/trigger.ts +0 -41
- package/src/core/instructions/withdraw.ts +0 -65
- package/src/core/remainingAccounts.ts +0 -320
- package/src/core/signedMsg.ts +0 -109
- package/src/decode/customCoder.ts +0 -148
- package/src/decode/user.ts +0 -401
- package/src/dlob/DLOB.ts +0 -2793
- package/src/dlob/DLOBNode.ts +0 -305
- package/src/dlob/DLOBSubscriber.ts +0 -269
- package/src/dlob/NodeList.ts +0 -244
- package/src/dlob/orderBookLevels.ts +0 -695
- package/src/dlob/types.ts +0 -37
- package/src/events/eventList.ts +0 -114
- package/src/events/eventSubscriber.ts +0 -521
- package/src/events/eventsServerLogProvider.ts +0 -189
- package/src/events/fetchLogs.ts +0 -219
- package/src/events/parse.ts +0 -271
- package/src/events/pollingLogProvider.ts +0 -113
- package/src/events/sort.ts +0 -51
- package/src/events/txEventCache.ts +0 -92
- package/src/events/types.ts +0 -337
- package/src/events/webSocketLogProvider.ts +0 -152
- package/src/factory/bigNum.ts +0 -709
- package/src/factory/oracleClient.ts +0 -73
- package/src/idl/pyth.json +0 -142
- package/src/idl/pyth.ts +0 -97
- package/src/idl/pyth_solana_receiver.json +0 -628
- package/src/idl/token_faucet.json +0 -229
- package/src/idl/token_faucet.ts +0 -197
- package/src/idl/velocity.json +0 -23974
- package/src/idl/velocity.ts +0 -23980
- package/src/index.ts +0 -150
- package/src/indicative-quotes/indicativeQuotesSender.ts +0 -233
- package/src/isomorphic/README.md +0 -19
- package/src/isomorphic/anchor.browser.ts +0 -44
- package/src/isomorphic/anchor.node.ts +0 -1
- package/src/isomorphic/anchor.ts +0 -1
- package/src/isomorphic/anchor29.browser.ts +0 -24
- package/src/isomorphic/anchor29.node.ts +0 -1
- package/src/isomorphic/anchor29.ts +0 -1
- package/src/isomorphic/grpc.browser.ts +0 -4
- package/src/isomorphic/grpc.node.ts +0 -130
- package/src/isomorphic/grpc.ts +0 -1
- package/src/jupiter/jupiterClient.ts +0 -502
- package/src/keypair.ts +0 -32
- package/src/margin/README.md +0 -138
- package/src/marginCalculation.ts +0 -407
- package/src/marinade/idl/idl.json +0 -1962
- package/src/marinade/index.ts +0 -73
- package/src/marinade/types.ts +0 -3925
- package/src/math/amm.ts +0 -1758
- package/src/math/auction.ts +0 -480
- package/src/math/bankruptcy.ts +0 -138
- package/src/math/builder.ts +0 -130
- package/src/math/conversion.ts +0 -45
- package/src/math/exchangeStatus.ts +0 -261
- package/src/math/funding.ts +0 -498
- package/src/math/insurance.ts +0 -171
- package/src/math/liquidation.ts +0 -433
- package/src/math/margin.ts +0 -327
- package/src/math/market.ts +0 -487
- package/src/math/oracles.ts +0 -477
- package/src/math/orders.ts +0 -500
- package/src/math/position.ts +0 -381
- package/src/math/repeg.ts +0 -223
- package/src/math/spotBalance.ts +0 -996
- package/src/math/spotMarket.ts +0 -115
- package/src/math/spotPosition.ts +0 -263
- package/src/math/state.ts +0 -71
- package/src/math/superStake.ts +0 -602
- package/src/math/tiers.ts +0 -73
- package/src/math/trade.ts +0 -879
- package/src/math/utils.ts +0 -183
- package/src/memcmp.ts +0 -279
- package/src/oracles/oracleClientCache.ts +0 -41
- package/src/oracles/oracleId.ts +0 -99
- package/src/oracles/prelaunchOracleClient.ts +0 -63
- package/src/oracles/pythClient.ts +0 -125
- package/src/oracles/pythLazerClient.ts +0 -141
- package/src/oracles/quoteAssetOracleClient.ts +0 -43
- package/src/oracles/strictOraclePrice.ts +0 -40
- package/src/oracles/types.ts +0 -82
- package/src/oracles/utils.ts +0 -45
- package/src/orderParams.ts +0 -117
- package/src/orderSubscriber/OrderSubscriber.ts +0 -378
- package/src/orderSubscriber/PollingSubscription.ts +0 -50
- package/src/orderSubscriber/WebsocketSubscription.ts +0 -137
- package/src/orderSubscriber/grpcSubscription.ts +0 -155
- package/src/orderSubscriber/index.ts +0 -7
- package/src/orderSubscriber/types.ts +0 -64
- package/src/priorityFee/averageOverSlotsStrategy.ts +0 -17
- package/src/priorityFee/averageStrategy.ts +0 -13
- package/src/priorityFee/ewmaStrategy.ts +0 -51
- package/src/priorityFee/heliusPriorityFeeMethod.ts +0 -68
- package/src/priorityFee/index.ts +0 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +0 -18
- package/src/priorityFee/maxStrategy.ts +0 -8
- package/src/priorityFee/priorityFeeSubscriber.ts +0 -328
- package/src/priorityFee/priorityFeeSubscriberMap.ts +0 -134
- package/src/priorityFee/solanaPriorityFeeMethod.ts +0 -46
- package/src/priorityFee/types.ts +0 -82
- package/src/priorityFee/velocityPriorityFeeMethod.ts +0 -53
- package/src/pyth/constants.ts +0 -9
- package/src/pyth/index.ts +0 -15
- package/src/pyth/pythLazerSubscriber.ts +0 -365
- package/src/pyth/types.ts +0 -4451
- package/src/pyth/utils.ts +0 -13
- package/src/slot/SlotSubscriber.ts +0 -126
- package/src/slot/SlothashSubscriber.ts +0 -163
- package/src/swap/UnifiedSwapClient.ts +0 -315
- package/src/swift/grpcSignedMsgUserAccountSubscriber.ts +0 -81
- package/src/swift/index.ts +0 -10
- package/src/swift/signedMsgUserAccountSubscriber.ts +0 -251
- package/src/swift/swiftOrderSubscriber.ts +0 -351
- package/src/testClient.ts +0 -42
- package/src/titan/titanClient.ts +0 -438
- package/src/token/index.ts +0 -13
- package/src/tokenFaucet.ts +0 -296
- package/src/tx/baseTxSender.ts +0 -651
- package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +0 -31
- package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +0 -105
- package/src/tx/blockhashFetcher/types.ts +0 -9
- package/src/tx/fastSingleTxSender.ts +0 -200
- package/src/tx/forwardOnlyTxSender.ts +0 -193
- package/src/tx/priorityFeeCalculator.ts +0 -117
- package/src/tx/reportTransactionError.ts +0 -176
- package/src/tx/retryTxSender.ts +0 -177
- package/src/tx/txHandler.ts +0 -1009
- package/src/tx/txParamProcessor.ts +0 -225
- package/src/tx/types.ts +0 -117
- package/src/tx/utils.ts +0 -133
- package/src/tx/whileValidTxSender.ts +0 -377
- package/src/types.ts +0 -2575
- package/src/user.ts +0 -5316
- package/src/userConfig.ts +0 -38
- package/src/userMap/PollingSubscription.ts +0 -61
- package/src/userMap/WebsocketSubscription.ts +0 -93
- package/src/userMap/grpcSubscription.ts +0 -93
- package/src/userMap/referrerMap.ts +0 -360
- package/src/userMap/revenueShareEscrowMap.ts +0 -342
- package/src/userMap/userMap.ts +0 -784
- package/src/userMap/userMapConfig.ts +0 -82
- package/src/userMap/userStatsMap.ts +0 -393
- package/src/userName.ts +0 -37
- package/src/userStats.ts +0 -159
- package/src/userStatsConfig.ts +0 -35
- package/src/util/TransactionConfirmationManager.ts +0 -333
- package/src/util/chainClock.ts +0 -65
- package/src/util/computeUnits.ts +0 -101
- package/src/util/digest.ts +0 -33
- package/src/util/ed25519Utils.ts +0 -95
- package/src/util/promiseTimeout.ts +0 -23
- package/src/velocityClient.ts +0 -14802
- package/src/velocityClientConfig.ts +0 -119
- package/src/wallet.ts +0 -91
- package/tests/VelocityCore/builder_escrow.test.ts +0 -121
- package/tests/VelocityCore/decode.test.ts +0 -16
- package/tests/VelocityCore/fill_trigger.test.ts +0 -73
- package/tests/VelocityCore/instructions.test.ts +0 -46
- package/tests/VelocityCore/pdas.test.ts +0 -30
- package/tests/VelocityCore/perp_orders.test.ts +0 -205
- package/tests/VelocityCore/remainingAccounts.test.ts +0 -72
- package/tests/VelocityCore/settlement_liquidation.test.ts +0 -69
- package/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +0 -211
- package/tests/amm/test.ts +0 -2402
- package/tests/auctions/test.ts +0 -81
- package/tests/bn/test.ts +0 -355
- package/tests/builder/builderFee.test.ts +0 -42
- package/tests/ci/verifyConstants.ts +0 -393
- package/tests/decode/test.ts +0 -262
- package/tests/decode/userAccountBufferStrings.ts +0 -102
- package/tests/dlob/helpers.ts +0 -748
- package/tests/dlob/test.ts +0 -6969
- package/tests/dlob/tickSizeStandardization.ts +0 -545
- package/tests/events/parseLogsForCuUsage.ts +0 -139
- package/tests/exchangeStatus/test.ts +0 -45
- package/tests/insurance/test.ts +0 -40
- package/tests/liquidation/test.ts +0 -125
- package/tests/oracles/mmOracleGate.test.ts +0 -379
- package/tests/oracles/pythPegSnap.test.ts +0 -76
- package/tests/sdkParity/enumParity.test.ts +0 -84
- package/tests/sdkParity/marginCategoryFill.test.ts +0 -143
- package/tests/sdkParity/memcmpOffsets.test.ts +0 -139
- package/tests/spot/test.ts +0 -281
- package/tests/tx/TransactionConfirmationManager.test.ts +0 -312
- package/tests/tx/cachedBlockhashFetcher.test.ts +0 -96
- package/tests/tx/priorityFeeCalculator.ts +0 -77
- package/tests/tx/priorityFeeStrategy.ts +0 -95
- package/tests/user/bankruptcy.ts +0 -165
- package/tests/user/feeAndWithdrawLimits.ts +0 -284
- package/tests/user/getMarginCalculation.ts +0 -586
- package/tests/user/helpers.ts +0 -184
- package/tests/user/liquidations.ts +0 -129
- package/tests/user/marginCalculations.test.ts +0 -321
- package/tests/user/test.ts +0 -833
- package/tsconfig.browser.json +0 -18
- package/tsconfig.json +0 -36
package/src/math/spotBalance.ts
DELETED
|
@@ -1,996 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SpotMarketAccount,
|
|
3
|
-
SpotBalanceType,
|
|
4
|
-
isVariant,
|
|
5
|
-
MarginCategory,
|
|
6
|
-
} from '../types';
|
|
7
|
-
import { BN } from '../isomorphic/anchor';
|
|
8
|
-
import {
|
|
9
|
-
SPOT_MARKET_UTILIZATION_PRECISION,
|
|
10
|
-
ONE,
|
|
11
|
-
TEN,
|
|
12
|
-
ZERO,
|
|
13
|
-
SPOT_MARKET_RATE_PRECISION,
|
|
14
|
-
SPOT_MARKET_WEIGHT_PRECISION,
|
|
15
|
-
ONE_YEAR,
|
|
16
|
-
AMM_RESERVE_PRECISION,
|
|
17
|
-
QUOTE_SPOT_MARKET_INDEX,
|
|
18
|
-
} from '../constants/numericConstants';
|
|
19
|
-
import {
|
|
20
|
-
calculateSizeDiscountAssetWeight,
|
|
21
|
-
calculateSizePremiumLiabilityWeight,
|
|
22
|
-
} from './margin';
|
|
23
|
-
import { OraclePriceData } from '../oracles/types';
|
|
24
|
-
import { PERCENTAGE_PRECISION } from '../constants/numericConstants';
|
|
25
|
-
import { divCeil } from './utils';
|
|
26
|
-
import { StrictOraclePrice } from '../oracles/strictOraclePrice';
|
|
27
|
-
|
|
28
|
-
// BN's `.div()` truncates toward zero; the program uses `safe_div_floor` when
|
|
29
|
-
// the numerator is negative (get_token_value / get_strict_token_value), so a
|
|
30
|
-
// negative dividend must round toward -infinity here to match.
|
|
31
|
-
function divFloor(a: BN, b: BN): BN {
|
|
32
|
-
const quotient = a.div(b);
|
|
33
|
-
const remainder = a.mod(b);
|
|
34
|
-
if (!remainder.isZero() && a.isNeg() !== b.isNeg()) {
|
|
35
|
-
return quotient.sub(ONE);
|
|
36
|
-
}
|
|
37
|
-
return quotient;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Calculates the balance of a given token amount including any accumulated interest. This
|
|
42
|
-
* is the same as `SpotPosition.scaledBalance`.
|
|
43
|
-
*
|
|
44
|
-
* @param {BN} tokenAmount - the amount of tokens
|
|
45
|
-
* @param {SpotMarketAccount} spotMarket - the spot market account
|
|
46
|
-
* @param {SpotBalanceType} balanceType - the balance type ('deposit' or 'borrow')
|
|
47
|
-
* @param {boolean} [roundUp] - override the default rounding direction (program's `round_up`);
|
|
48
|
-
* defaults to rounding up for borrows only. Callers reducing a deposit balance while the
|
|
49
|
-
* funds are leaving Velocity (e.g. a withdrawal) should pass `true` to match `is_leaving_velocity`.
|
|
50
|
-
* @return {BN} the calculated balance, scaled by `SPOT_MARKET_BALANCE_PRECISION`
|
|
51
|
-
*/
|
|
52
|
-
export function getBalance(
|
|
53
|
-
tokenAmount: BN,
|
|
54
|
-
spotMarket: SpotMarketAccount,
|
|
55
|
-
balanceType: SpotBalanceType,
|
|
56
|
-
roundUp?: boolean
|
|
57
|
-
): BN {
|
|
58
|
-
const precisionIncrease = TEN.pow(new BN(19 - spotMarket.decimals));
|
|
59
|
-
|
|
60
|
-
const cumulativeInterest = isVariant(balanceType, 'deposit')
|
|
61
|
-
? spotMarket.cumulativeDepositInterest
|
|
62
|
-
: spotMarket.cumulativeBorrowInterest;
|
|
63
|
-
|
|
64
|
-
let balance = tokenAmount.mul(precisionIncrease).div(cumulativeInterest);
|
|
65
|
-
|
|
66
|
-
const shouldRoundUp = roundUp ?? isVariant(balanceType, 'borrow');
|
|
67
|
-
if (!balance.eq(ZERO) && shouldRoundUp) {
|
|
68
|
-
balance = balance.add(ONE);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return balance;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Calculates the spot token amount including any accumulated interest.
|
|
76
|
-
*
|
|
77
|
-
* @param {BN} balanceAmount - The balance amount, typically from `SpotPosition.scaledBalance`
|
|
78
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account details
|
|
79
|
-
* @param {SpotBalanceType} balanceType - The balance type to be used for calculation
|
|
80
|
-
* @returns {BN} The calculated token amount, scaled by `SpotMarketConfig.precision`
|
|
81
|
-
*/
|
|
82
|
-
export function getTokenAmount(
|
|
83
|
-
balanceAmount: BN,
|
|
84
|
-
spotMarket: SpotMarketAccount,
|
|
85
|
-
balanceType: SpotBalanceType
|
|
86
|
-
): BN {
|
|
87
|
-
const precisionDecrease = TEN.pow(new BN(19 - spotMarket.decimals));
|
|
88
|
-
if (isVariant(balanceType, 'deposit')) {
|
|
89
|
-
return balanceAmount
|
|
90
|
-
.mul(spotMarket.cumulativeDepositInterest)
|
|
91
|
-
.div(precisionDecrease);
|
|
92
|
-
} else {
|
|
93
|
-
return divCeil(
|
|
94
|
-
balanceAmount.mul(spotMarket.cumulativeBorrowInterest),
|
|
95
|
-
precisionDecrease
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Returns the signed (positive for deposit,negative for borrow) token amount based on the balance type.
|
|
102
|
-
*
|
|
103
|
-
* @param {BN} tokenAmount - The token amount to convert (from `getTokenAmount`)
|
|
104
|
-
* @param {SpotBalanceType} balanceType - The balance type to determine the sign of the token amount.
|
|
105
|
-
* @returns {BN} - The signed token amount, scaled by `SpotMarketConfig.precision`
|
|
106
|
-
*/
|
|
107
|
-
export function getSignedTokenAmount(
|
|
108
|
-
tokenAmount: BN,
|
|
109
|
-
balanceType: SpotBalanceType
|
|
110
|
-
): BN {
|
|
111
|
-
if (isVariant(balanceType, 'deposit')) {
|
|
112
|
-
return tokenAmount;
|
|
113
|
-
} else {
|
|
114
|
-
return tokenAmount.abs().neg();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Calculates the value of a given token amount using the worst of the provided oracle price and its TWAP.
|
|
120
|
-
*
|
|
121
|
-
* @param {BN} tokenAmount - The amount of tokens to calculate the value for (from `getTokenAmount`)
|
|
122
|
-
* @param {number} spotDecimals - The number of decimals in the token.
|
|
123
|
-
* @param {StrictOraclePrice} strictOraclePrice - Contains oracle price and 5min twap.
|
|
124
|
-
* @return {BN} The calculated value of the given token amount, scaled by `PRICE_PRECISION`
|
|
125
|
-
*/
|
|
126
|
-
export function getStrictTokenValue(
|
|
127
|
-
tokenAmount: BN,
|
|
128
|
-
spotDecimals: number,
|
|
129
|
-
strictOraclePrice: StrictOraclePrice
|
|
130
|
-
): BN {
|
|
131
|
-
if (tokenAmount.eq(ZERO)) {
|
|
132
|
-
return ZERO;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let price;
|
|
136
|
-
if (tokenAmount.gte(ZERO)) {
|
|
137
|
-
price = strictOraclePrice.min();
|
|
138
|
-
} else {
|
|
139
|
-
price = strictOraclePrice.max();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const precisionDecrease = TEN.pow(new BN(spotDecimals));
|
|
143
|
-
const tokenWithPrice = tokenAmount.mul(price);
|
|
144
|
-
|
|
145
|
-
if (tokenWithPrice.isNeg()) {
|
|
146
|
-
return divFloor(tokenWithPrice, precisionDecrease);
|
|
147
|
-
}
|
|
148
|
-
return tokenWithPrice.div(precisionDecrease);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Calculates the value of a given token amount in relation to an oracle price data
|
|
153
|
-
*
|
|
154
|
-
* @param {BN} tokenAmount - The amount of tokens to calculate the value for (from `getTokenAmount`)
|
|
155
|
-
* @param {number} spotDecimals - The number of decimal places of the token.
|
|
156
|
-
* @param {OraclePriceData} oraclePriceData - The oracle price data (typically a token/USD oracle).
|
|
157
|
-
* @return {BN} The value of the token based on the oracle, scaled by `PRICE_PRECISION`
|
|
158
|
-
*/
|
|
159
|
-
export function getTokenValue(
|
|
160
|
-
tokenAmount: BN,
|
|
161
|
-
spotDecimals: number,
|
|
162
|
-
oraclePriceData: Pick<OraclePriceData, 'price'>
|
|
163
|
-
): BN {
|
|
164
|
-
if (tokenAmount.eq(ZERO)) {
|
|
165
|
-
return ZERO;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const precisionDecrease = TEN.pow(new BN(spotDecimals));
|
|
169
|
-
const tokenWithOraclePrice = tokenAmount.mul(oraclePriceData.price);
|
|
170
|
-
|
|
171
|
-
if (tokenWithOraclePrice.isNeg()) {
|
|
172
|
-
return divFloor(tokenWithOraclePrice, precisionDecrease);
|
|
173
|
-
}
|
|
174
|
-
return tokenWithOraclePrice.div(precisionDecrease);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Calculates the collateral (asset) weight applied to a spot deposit balance, mirroring
|
|
179
|
-
* `SpotMarket::get_asset_weight`'s `Initial`/`Maintenance` branches (there is no SDK
|
|
180
|
-
* equivalent of the on-chain `Fill` branch, which averages initial and maintenance).
|
|
181
|
-
* Size is first rescaled into `AMM_RESERVE_PRECISION` before the IMF size-discount is applied,
|
|
182
|
-
* so larger positions receive a lower (more conservative) weight.
|
|
183
|
-
*
|
|
184
|
-
* @param {BN} balanceAmount - The deposit token amount, scaled by the spot market's token decimals
|
|
185
|
-
* @param {BN} oraclePrice - The oracle price, PRICE_PRECISION (1e6); only used for the `Initial`
|
|
186
|
-
* scaled-weight lookup (`calculateScaledInitialAssetWeight`)
|
|
187
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account
|
|
188
|
-
* @param {MarginCategory | undefined} marginCategory - `'Initial'`, `'Maintenance'`, `'Fill'`
|
|
189
|
-
* (the integer-averaged midpoint of scaled-initial and maintenance weights), or `undefined`
|
|
190
|
-
* (defaults to the scaled initial weight, used for e.g. UI display outside a margin check)
|
|
191
|
-
* @return {BN} The asset weight, scaled by `SPOT_MARKET_WEIGHT_PRECISION` (1e4, i.e. 10000 = 100%)
|
|
192
|
-
*/
|
|
193
|
-
export function calculateAssetWeight(
|
|
194
|
-
balanceAmount: BN,
|
|
195
|
-
oraclePrice: BN,
|
|
196
|
-
spotMarket: SpotMarketAccount,
|
|
197
|
-
marginCategory: MarginCategory | undefined
|
|
198
|
-
): BN {
|
|
199
|
-
const sizePrecision = TEN.pow(new BN(spotMarket.decimals));
|
|
200
|
-
let sizeInAmmReservePrecision;
|
|
201
|
-
if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
|
|
202
|
-
sizeInAmmReservePrecision = balanceAmount.div(
|
|
203
|
-
sizePrecision.div(AMM_RESERVE_PRECISION)
|
|
204
|
-
);
|
|
205
|
-
} else {
|
|
206
|
-
sizeInAmmReservePrecision = balanceAmount
|
|
207
|
-
.mul(AMM_RESERVE_PRECISION)
|
|
208
|
-
.div(sizePrecision);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
let assetWeight;
|
|
212
|
-
|
|
213
|
-
switch (marginCategory) {
|
|
214
|
-
case 'Initial':
|
|
215
|
-
assetWeight = calculateSizeDiscountAssetWeight(
|
|
216
|
-
sizeInAmmReservePrecision,
|
|
217
|
-
new BN(spotMarket.imfFactor),
|
|
218
|
-
calculateScaledInitialAssetWeight(spotMarket, oraclePrice)
|
|
219
|
-
);
|
|
220
|
-
break;
|
|
221
|
-
case 'Fill':
|
|
222
|
-
// mirrors SpotMarket::get_asset_weight's Fill branch:
|
|
223
|
-
// (scaled_initial_asset_weight + maintenance_asset_weight) / 2 (integer division)
|
|
224
|
-
assetWeight = calculateSizeDiscountAssetWeight(
|
|
225
|
-
sizeInAmmReservePrecision,
|
|
226
|
-
new BN(spotMarket.imfFactor),
|
|
227
|
-
calculateScaledInitialAssetWeight(spotMarket, oraclePrice)
|
|
228
|
-
.add(new BN(spotMarket.maintenanceAssetWeight))
|
|
229
|
-
.divn(2)
|
|
230
|
-
);
|
|
231
|
-
break;
|
|
232
|
-
case 'Maintenance':
|
|
233
|
-
assetWeight = calculateSizeDiscountAssetWeight(
|
|
234
|
-
sizeInAmmReservePrecision,
|
|
235
|
-
new BN(spotMarket.imfFactor),
|
|
236
|
-
new BN(spotMarket.maintenanceAssetWeight)
|
|
237
|
-
);
|
|
238
|
-
break;
|
|
239
|
-
default:
|
|
240
|
-
assetWeight = calculateScaledInitialAssetWeight(spotMarket, oraclePrice);
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return assetWeight;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Calculates the initial asset weight after applying the market's optional deposit-value
|
|
249
|
-
* scaling, mirroring `SpotMarket::get_scaled_initial_asset_weight`. When
|
|
250
|
-
* `scaleInitialAssetWeightStart` is set and total deposit value exceeds it, the weight is
|
|
251
|
-
* scaled down proportionally (`initialAssetWeight * scaleInitialAssetWeightStart / depositsValue`)
|
|
252
|
-
* so the market's collateral usefulness degrades as its deposits grow past the configured cap.
|
|
253
|
-
*
|
|
254
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account
|
|
255
|
-
* @param {BN} oraclePrice - The oracle price, PRICE_PRECISION (1e6), used to value total deposits
|
|
256
|
-
* @return {BN} The (possibly scaled) initial asset weight, `SPOT_MARKET_WEIGHT_PRECISION` (1e4)
|
|
257
|
-
*/
|
|
258
|
-
export function calculateScaledInitialAssetWeight(
|
|
259
|
-
spotMarket: SpotMarketAccount,
|
|
260
|
-
oraclePrice: BN
|
|
261
|
-
): BN {
|
|
262
|
-
if (spotMarket.scaleInitialAssetWeightStart.eq(ZERO)) {
|
|
263
|
-
return new BN(spotMarket.initialAssetWeight);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const deposits = getTokenAmount(
|
|
267
|
-
spotMarket.depositBalance,
|
|
268
|
-
spotMarket,
|
|
269
|
-
SpotBalanceType.DEPOSIT
|
|
270
|
-
);
|
|
271
|
-
const depositsValue = getTokenValue(deposits, spotMarket.decimals, {
|
|
272
|
-
price: oraclePrice,
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
if (depositsValue.lt(spotMarket.scaleInitialAssetWeightStart)) {
|
|
276
|
-
return new BN(spotMarket.initialAssetWeight);
|
|
277
|
-
} else {
|
|
278
|
-
return new BN(spotMarket.initialAssetWeight)
|
|
279
|
-
.mul(spotMarket.scaleInitialAssetWeightStart)
|
|
280
|
-
.div(depositsValue);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Calculates the liability (borrow) weight applied to a spot borrow balance, mirroring
|
|
286
|
-
* `SpotMarket::get_liability_weight`'s `Initial`/`Maintenance` branches. Size is rescaled into
|
|
287
|
-
* `AMM_RESERVE_PRECISION` before the IMF size-premium is applied, so larger borrows receive a
|
|
288
|
-
* higher (more conservative) weight.
|
|
289
|
-
*
|
|
290
|
-
* @param {BN} size - The borrow token amount, scaled by the spot market's token decimals
|
|
291
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account
|
|
292
|
-
* @param {MarginCategory | undefined} marginCategory - `'Initial'`, `'Maintenance'`, `'Fill'`
|
|
293
|
-
* (the integer-averaged midpoint of initial and maintenance liability weights), or
|
|
294
|
-
* `undefined` (defaults to `initialLiabilityWeight` with no size premium applied)
|
|
295
|
-
* @return {BN} The liability weight, scaled by `SPOT_MARKET_WEIGHT_PRECISION` (1e4, i.e. 10000 = 100%)
|
|
296
|
-
*/
|
|
297
|
-
export function calculateLiabilityWeight(
|
|
298
|
-
size: BN,
|
|
299
|
-
spotMarket: SpotMarketAccount,
|
|
300
|
-
marginCategory: MarginCategory | undefined
|
|
301
|
-
): BN {
|
|
302
|
-
const sizePrecision = TEN.pow(new BN(spotMarket.decimals));
|
|
303
|
-
let sizeInAmmReservePrecision;
|
|
304
|
-
if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
|
|
305
|
-
sizeInAmmReservePrecision = size.div(
|
|
306
|
-
sizePrecision.div(AMM_RESERVE_PRECISION)
|
|
307
|
-
);
|
|
308
|
-
} else {
|
|
309
|
-
sizeInAmmReservePrecision = size
|
|
310
|
-
.mul(AMM_RESERVE_PRECISION)
|
|
311
|
-
.div(sizePrecision);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
let liabilityWeight;
|
|
315
|
-
|
|
316
|
-
switch (marginCategory) {
|
|
317
|
-
case 'Initial':
|
|
318
|
-
liabilityWeight = calculateSizePremiumLiabilityWeight(
|
|
319
|
-
sizeInAmmReservePrecision,
|
|
320
|
-
new BN(spotMarket.imfFactor),
|
|
321
|
-
new BN(spotMarket.initialLiabilityWeight),
|
|
322
|
-
SPOT_MARKET_WEIGHT_PRECISION
|
|
323
|
-
);
|
|
324
|
-
break;
|
|
325
|
-
case 'Fill':
|
|
326
|
-
// mirrors SpotMarket::get_liability_weight's Fill branch:
|
|
327
|
-
// (initial_liability_weight + maintenance_liability_weight) / 2 (integer division)
|
|
328
|
-
liabilityWeight = calculateSizePremiumLiabilityWeight(
|
|
329
|
-
sizeInAmmReservePrecision,
|
|
330
|
-
new BN(spotMarket.imfFactor),
|
|
331
|
-
new BN(spotMarket.initialLiabilityWeight)
|
|
332
|
-
.add(new BN(spotMarket.maintenanceLiabilityWeight))
|
|
333
|
-
.divn(2),
|
|
334
|
-
SPOT_MARKET_WEIGHT_PRECISION
|
|
335
|
-
);
|
|
336
|
-
break;
|
|
337
|
-
case 'Maintenance':
|
|
338
|
-
liabilityWeight = calculateSizePremiumLiabilityWeight(
|
|
339
|
-
sizeInAmmReservePrecision,
|
|
340
|
-
new BN(spotMarket.imfFactor),
|
|
341
|
-
new BN(spotMarket.maintenanceLiabilityWeight),
|
|
342
|
-
SPOT_MARKET_WEIGHT_PRECISION
|
|
343
|
-
);
|
|
344
|
-
break;
|
|
345
|
-
default:
|
|
346
|
-
liabilityWeight = new BN(spotMarket.initialLiabilityWeight);
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return liabilityWeight;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Calculates a spot market's utilization (borrows / deposits), mirroring
|
|
355
|
-
* `calculate_utilization`. Returns `SPOT_MARKET_UTILIZATION_PRECISION` (100% utilization) if
|
|
356
|
-
* there are borrows but no deposits, and zero if both are zero.
|
|
357
|
-
*
|
|
358
|
-
* @param {SpotMarketAccount} bank - The spot market account
|
|
359
|
-
* @param {BN} [delta] - Optional hypothetical change in token amount, scaled by the market's
|
|
360
|
-
* token decimals: a positive delta is added to deposits, a negative delta (its absolute
|
|
361
|
-
* value) is added to borrows. Defaults to zero (current on-chain utilization).
|
|
362
|
-
* @return {BN} Utilization, scaled by `SPOT_MARKET_UTILIZATION_PRECISION` (1e6, i.e. 1e6 = 100%)
|
|
363
|
-
*/
|
|
364
|
-
export function calculateUtilization(
|
|
365
|
-
bank: SpotMarketAccount,
|
|
366
|
-
delta = ZERO
|
|
367
|
-
): BN {
|
|
368
|
-
let tokenDepositAmount = getTokenAmount(
|
|
369
|
-
bank.depositBalance,
|
|
370
|
-
bank,
|
|
371
|
-
SpotBalanceType.DEPOSIT
|
|
372
|
-
);
|
|
373
|
-
let tokenBorrowAmount = getTokenAmount(
|
|
374
|
-
bank.borrowBalance,
|
|
375
|
-
bank,
|
|
376
|
-
SpotBalanceType.BORROW
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
if (delta.gt(ZERO)) {
|
|
380
|
-
tokenDepositAmount = tokenDepositAmount.add(delta);
|
|
381
|
-
} else if (delta.lt(ZERO)) {
|
|
382
|
-
tokenBorrowAmount = tokenBorrowAmount.add(delta.abs());
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
let utilization: BN;
|
|
386
|
-
if (tokenBorrowAmount.eq(ZERO) && tokenDepositAmount.eq(ZERO)) {
|
|
387
|
-
utilization = ZERO;
|
|
388
|
-
} else if (tokenDepositAmount.eq(ZERO)) {
|
|
389
|
-
utilization = SPOT_MARKET_UTILIZATION_PRECISION;
|
|
390
|
-
} else {
|
|
391
|
-
utilization = tokenBorrowAmount
|
|
392
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
393
|
-
.div(tokenDepositAmount);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return utilization;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* SDK-only helper (no direct on-chain counterpart) that inverts `calculateInterestRate`'s
|
|
401
|
-
* utilization curve to find how much more can be borrowed before the borrow rate would reach
|
|
402
|
-
* `targetBorrowRate`. Useful for UI "available to borrow at rate X" displays.
|
|
403
|
-
*
|
|
404
|
-
* @param {SpotMarketAccount} spotMarketAccount - The spot market account
|
|
405
|
-
* @param {BN} targetBorrowRate - The target annualized borrow rate, `SPOT_MARKET_RATE_PRECISION` (1e6)
|
|
406
|
-
* @returns {{ totalCapacity: BN; remainingCapacity: BN }} Both scaled by the market's token
|
|
407
|
-
* decimals. `totalCapacity` is the total borrow amount implied by the target utilization;
|
|
408
|
-
* `remainingCapacity` is `totalCapacity` minus current borrows (zero if the market's current
|
|
409
|
-
* borrow rate already meets or exceeds the target), additionally capped by
|
|
410
|
-
* `maxTokenBorrowsFraction` of `maxTokenDeposits` when that cap is configured (>0)
|
|
411
|
-
*/
|
|
412
|
-
export function calculateSpotMarketBorrowCapacity(
|
|
413
|
-
spotMarketAccount: SpotMarketAccount,
|
|
414
|
-
targetBorrowRate: BN
|
|
415
|
-
): { totalCapacity: BN; remainingCapacity: BN } {
|
|
416
|
-
const currentBorrowRate = calculateBorrowRate(spotMarketAccount);
|
|
417
|
-
|
|
418
|
-
const tokenDepositAmount = getTokenAmount(
|
|
419
|
-
spotMarketAccount.depositBalance,
|
|
420
|
-
spotMarketAccount,
|
|
421
|
-
SpotBalanceType.DEPOSIT
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
const tokenBorrowAmount = getTokenAmount(
|
|
425
|
-
spotMarketAccount.borrowBalance,
|
|
426
|
-
spotMarketAccount,
|
|
427
|
-
SpotBalanceType.BORROW
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
let targetUtilization;
|
|
431
|
-
// target utilization past mid point
|
|
432
|
-
if (targetBorrowRate.gte(new BN(spotMarketAccount.optimalBorrowRate))) {
|
|
433
|
-
const borrowRateSlope = new BN(
|
|
434
|
-
spotMarketAccount.maxBorrowRate - spotMarketAccount.optimalBorrowRate
|
|
435
|
-
)
|
|
436
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
437
|
-
.div(
|
|
438
|
-
SPOT_MARKET_UTILIZATION_PRECISION.sub(
|
|
439
|
-
new BN(spotMarketAccount.optimalUtilization)
|
|
440
|
-
)
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
const surplusTargetUtilization = targetBorrowRate
|
|
444
|
-
.sub(new BN(spotMarketAccount.optimalBorrowRate))
|
|
445
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
446
|
-
.div(borrowRateSlope);
|
|
447
|
-
|
|
448
|
-
targetUtilization = surplusTargetUtilization.add(
|
|
449
|
-
new BN(spotMarketAccount.optimalUtilization)
|
|
450
|
-
);
|
|
451
|
-
} else {
|
|
452
|
-
const borrowRateSlope = new BN(spotMarketAccount.optimalBorrowRate)
|
|
453
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
454
|
-
.div(new BN(spotMarketAccount.optimalUtilization));
|
|
455
|
-
|
|
456
|
-
targetUtilization = targetBorrowRate
|
|
457
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
458
|
-
.div(borrowRateSlope);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const totalCapacity = tokenDepositAmount
|
|
462
|
-
.mul(targetUtilization)
|
|
463
|
-
.div(SPOT_MARKET_UTILIZATION_PRECISION);
|
|
464
|
-
|
|
465
|
-
let remainingCapacity;
|
|
466
|
-
if (currentBorrowRate.gte(targetBorrowRate)) {
|
|
467
|
-
remainingCapacity = ZERO;
|
|
468
|
-
} else {
|
|
469
|
-
remainingCapacity = BN.max(ZERO, totalCapacity.sub(tokenBorrowAmount));
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (spotMarketAccount.maxTokenBorrowsFraction > 0) {
|
|
473
|
-
const maxTokenBorrows = spotMarketAccount.maxTokenDeposits
|
|
474
|
-
.mul(new BN(spotMarketAccount.maxTokenBorrowsFraction))
|
|
475
|
-
.divn(10000);
|
|
476
|
-
|
|
477
|
-
remainingCapacity = BN.min(
|
|
478
|
-
remainingCapacity,
|
|
479
|
-
BN.max(ZERO, maxTokenBorrows.sub(tokenBorrowAmount))
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return { totalCapacity, remainingCapacity };
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Calculates the annualized borrow interest rate for a spot market, mirroring
|
|
488
|
-
* `calculate_borrow_rate` / the underlying utilization curve. Below `optimalUtilization` the
|
|
489
|
-
* rate ramps linearly from 0 to `optimalBorrowRate`; above it, the rate ramps through a fixed
|
|
490
|
-
* piecewise schedule (85/90/95/99/99.5/100% utilization breakpoints) from `optimalBorrowRate`
|
|
491
|
-
* up to `maxBorrowRate`. The result is floored at `minBorrowRate / 200` (i.e. `minBorrowRate`
|
|
492
|
-
* is in units of half-percentage-points of `PERCENTAGE_PRECISION`).
|
|
493
|
-
*
|
|
494
|
-
* @param {SpotMarketAccount} bank - The spot market account
|
|
495
|
-
* @param {BN} [delta] - Optional hypothetical change in token amount passed through to
|
|
496
|
-
* `calculateUtilization` (ignored if `currentUtilization` is provided)
|
|
497
|
-
* @param {BN} [currentUtilization] - Precomputed utilization, `SPOT_MARKET_UTILIZATION_PRECISION`
|
|
498
|
-
* (1e6); if omitted it is derived from `bank` and `delta`
|
|
499
|
-
* @return {BN} Annualized borrow rate, scaled by `SPOT_MARKET_RATE_PRECISION` (1e6)
|
|
500
|
-
*/
|
|
501
|
-
export function calculateInterestRate(
|
|
502
|
-
bank: SpotMarketAccount,
|
|
503
|
-
delta = ZERO,
|
|
504
|
-
currentUtilization?: BN
|
|
505
|
-
): BN {
|
|
506
|
-
// todo: ensure both a delta and current util aren't pass?
|
|
507
|
-
const utilization = currentUtilization ?? calculateUtilization(bank, delta);
|
|
508
|
-
|
|
509
|
-
const optimalUtil = new BN(bank.optimalUtilization);
|
|
510
|
-
const optimalRate = new BN(bank.optimalBorrowRate);
|
|
511
|
-
const maxRate = new BN(bank.maxBorrowRate);
|
|
512
|
-
const minRate = new BN(bank.minBorrowRate).mul(
|
|
513
|
-
PERCENTAGE_PRECISION.divn(200)
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
const weightsDivisor = new BN(1000);
|
|
517
|
-
const segments: [BN, BN][] = [
|
|
518
|
-
[new BN(850_000), new BN(50)],
|
|
519
|
-
[new BN(900_000), new BN(100)],
|
|
520
|
-
[new BN(950_000), new BN(150)],
|
|
521
|
-
[new BN(990_000), new BN(200)],
|
|
522
|
-
[new BN(995_000), new BN(250)],
|
|
523
|
-
[SPOT_MARKET_UTILIZATION_PRECISION, new BN(250)],
|
|
524
|
-
];
|
|
525
|
-
|
|
526
|
-
let rate: BN;
|
|
527
|
-
if (utilization.lte(optimalUtil)) {
|
|
528
|
-
// below optimal: linear ramp from 0 to optimalRate
|
|
529
|
-
const slope = optimalRate
|
|
530
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
531
|
-
.div(optimalUtil);
|
|
532
|
-
rate = utilization.mul(slope).div(SPOT_MARKET_UTILIZATION_PRECISION);
|
|
533
|
-
} else {
|
|
534
|
-
// above optimal: piecewise segments
|
|
535
|
-
const totalExtraRate = maxRate.sub(optimalRate);
|
|
536
|
-
|
|
537
|
-
rate = optimalRate.clone();
|
|
538
|
-
let prevUtil = optimalUtil.clone();
|
|
539
|
-
|
|
540
|
-
for (const [bp, weight] of segments) {
|
|
541
|
-
const segmentEnd = bp.gt(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
542
|
-
? SPOT_MARKET_UTILIZATION_PRECISION
|
|
543
|
-
: bp;
|
|
544
|
-
const segmentRange = segmentEnd.sub(prevUtil);
|
|
545
|
-
|
|
546
|
-
const segmentRateTotal = totalExtraRate.mul(weight).div(weightsDivisor);
|
|
547
|
-
|
|
548
|
-
if (utilization.lte(segmentEnd)) {
|
|
549
|
-
const partialUtil = utilization.sub(prevUtil);
|
|
550
|
-
const partialRate = segmentRateTotal.mul(partialUtil).div(segmentRange);
|
|
551
|
-
rate = rate.add(partialRate);
|
|
552
|
-
break;
|
|
553
|
-
} else {
|
|
554
|
-
rate = rate.add(segmentRateTotal);
|
|
555
|
-
prevUtil = segmentEnd;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return BN.max(minRate, rate);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Calculates the annualized deposit interest rate for a spot market, mirroring
|
|
565
|
-
* `calculate_deposit_rate` (velocity-rs). Lenders receive the borrow rate net of the insurance
|
|
566
|
-
* fund and protocol fee carveouts (`ifFeeFactor` + `protocolFeeFactor`, both `PERCENTAGE_PRECISION`),
|
|
567
|
-
* scaled down by utilization since only borrowed deposits earn interest.
|
|
568
|
-
*
|
|
569
|
-
* @param {SpotMarketAccount} bank - The spot market account
|
|
570
|
-
* @param {BN} [delta] - Optional hypothetical change in token amount; positive adds to deposits,
|
|
571
|
-
* negative adds to borrows (see `calculateUtilization`)
|
|
572
|
-
* @param {BN} [currentUtilization] - Precomputed utilization, `SPOT_MARKET_UTILIZATION_PRECISION`
|
|
573
|
-
* (1e6); if omitted it is derived from `bank` and `delta`
|
|
574
|
-
* @return {BN} Annualized deposit rate, scaled by `SPOT_MARKET_RATE_PRECISION` (1e6)
|
|
575
|
-
*/
|
|
576
|
-
export function calculateDepositRate(
|
|
577
|
-
bank: SpotMarketAccount,
|
|
578
|
-
delta = ZERO,
|
|
579
|
-
currentUtilization?: BN
|
|
580
|
-
): BN {
|
|
581
|
-
// positive delta => adding to deposit
|
|
582
|
-
// negative delta => adding to borrow
|
|
583
|
-
|
|
584
|
-
const utilization = currentUtilization ?? calculateUtilization(bank, delta);
|
|
585
|
-
const borrowRate = calculateBorrowRate(bank, delta, utilization);
|
|
586
|
-
const depositRate = borrowRate
|
|
587
|
-
.mul(
|
|
588
|
-
PERCENTAGE_PRECISION.sub(
|
|
589
|
-
new BN(bank.insuranceFund.ifFeeFactor + bank.protocolFeeFactor)
|
|
590
|
-
)
|
|
591
|
-
)
|
|
592
|
-
.mul(utilization)
|
|
593
|
-
.div(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
594
|
-
.div(PERCENTAGE_PRECISION);
|
|
595
|
-
return depositRate;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Alias for `calculateInterestRate` (annualized borrow rate).
|
|
600
|
-
*
|
|
601
|
-
* @param {SpotMarketAccount} bank - The spot market account
|
|
602
|
-
* @param {BN} [delta] - Optional hypothetical change in token amount (see `calculateUtilization`)
|
|
603
|
-
* @param {BN} [currentUtilization] - Precomputed utilization, `SPOT_MARKET_UTILIZATION_PRECISION` (1e6)
|
|
604
|
-
* @return {BN} Annualized borrow rate, scaled by `SPOT_MARKET_RATE_PRECISION` (1e6)
|
|
605
|
-
*/
|
|
606
|
-
export function calculateBorrowRate(
|
|
607
|
-
bank: SpotMarketAccount,
|
|
608
|
-
delta = ZERO,
|
|
609
|
-
currentUtilization?: BN
|
|
610
|
-
): BN {
|
|
611
|
-
return calculateInterestRate(bank, delta, currentUtilization);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Projects the cumulative interest multipliers that would accrue between `spotMarket.lastInterestTs`
|
|
616
|
-
* and `now` at the market's current interest rate, mirroring the gross amounts computed by
|
|
617
|
-
* `calculate_accumulated_interest`. This is a point-in-time estimate for display purposes only —
|
|
618
|
-
* the actual on-chain update (`update_spot_market_cumulative_interest`) re-derives the rate from
|
|
619
|
-
* utilization at settlement time (same as this function calling `calculateInterestRate(bank)` with
|
|
620
|
-
* no delta), and only runs at all if `deposit_interest > 0 && borrow_interest > 1`. Borrow interest
|
|
621
|
-
* is always rounded up by 1 (added unconditionally), matching the program's lender-favoring
|
|
622
|
-
* rounding, and is credited to `cumulativeBorrowInterest` in full. **`depositInterest` here is the
|
|
623
|
-
* gross pre-carveout amount** — on-chain, `insuranceFund.ifFeeFactor` and `protocolFeeFactor`
|
|
624
|
-
* (both `IF_FACTOR_PRECISION`) are each cut from it first (to `revenuePool` and `protocolFeePool`
|
|
625
|
-
* respectively) and only the remainder is what actually gets added to `cumulativeDepositInterest`;
|
|
626
|
-
* this function does not replicate that split, so it overstates the deposit-side increment
|
|
627
|
-
* whenever either factor is non-zero.
|
|
628
|
-
*
|
|
629
|
-
* @param {SpotMarketAccount} bank - The spot market account
|
|
630
|
-
* @param {BN} now - The timestamp (unix seconds) to project interest up to
|
|
631
|
-
* @return {{ borrowInterest: BN; depositInterest: BN }} `borrowInterest` is the exact amount added
|
|
632
|
-
* to `cumulativeBorrowInterest`; `depositInterest` is the gross pre-carveout amount, not
|
|
633
|
-
* necessarily what's added to `cumulativeDepositInterest` (see above). Both in the same
|
|
634
|
-
* fixed-point units as those cumulative fields (`SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION`)
|
|
635
|
-
*/
|
|
636
|
-
export function calculateInterestAccumulated(
|
|
637
|
-
bank: SpotMarketAccount,
|
|
638
|
-
now: BN
|
|
639
|
-
): { borrowInterest: BN; depositInterest: BN } {
|
|
640
|
-
const interestRate = calculateInterestRate(bank);
|
|
641
|
-
|
|
642
|
-
const timeSinceLastUpdate = now.sub(bank.lastInterestTs);
|
|
643
|
-
|
|
644
|
-
const modifiedBorrowRate = interestRate.mul(timeSinceLastUpdate);
|
|
645
|
-
|
|
646
|
-
const utilization = calculateUtilization(bank);
|
|
647
|
-
|
|
648
|
-
const modifiedDepositRate = modifiedBorrowRate
|
|
649
|
-
.mul(utilization)
|
|
650
|
-
.div(SPOT_MARKET_UTILIZATION_PRECISION);
|
|
651
|
-
|
|
652
|
-
const borrowInterest = bank.cumulativeBorrowInterest
|
|
653
|
-
.mul(modifiedBorrowRate)
|
|
654
|
-
.div(ONE_YEAR)
|
|
655
|
-
.div(SPOT_MARKET_RATE_PRECISION)
|
|
656
|
-
.add(ONE);
|
|
657
|
-
const depositInterest = bank.cumulativeDepositInterest
|
|
658
|
-
.mul(modifiedDepositRate)
|
|
659
|
-
.div(ONE_YEAR)
|
|
660
|
-
.div(SPOT_MARKET_RATE_PRECISION);
|
|
661
|
-
|
|
662
|
-
return { borrowInterest, depositInterest };
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Calculates the minimum deposit / maximum borrow token amounts that keep the market's
|
|
667
|
-
* utilization from exceeding a "max withdraw utilization" ceiling, mirroring
|
|
668
|
-
* `calculate_token_utilization_limits`. The ceiling is `max(optimalUtilization,
|
|
669
|
-
* utilizationTwap + (100% - utilizationTwap) / 2)` — i.e. it allows utilization to rise, but
|
|
670
|
-
* only up to halfway from the TWAP to 100%. Deposit sizes already below
|
|
671
|
-
* `withdrawGuardThreshold` are never blocked (the min-deposit result is capped so it can't
|
|
672
|
-
* exceed `depositTokenAmount - withdrawGuardThreshold`), and borrows below the guard threshold
|
|
673
|
-
* are never blocked either (the max-borrow result is floored at `withdrawGuardThreshold`).
|
|
674
|
-
*
|
|
675
|
-
* @param {BN} depositTokenAmount - Current total deposit token amount, market's token decimals
|
|
676
|
-
* @param {BN} borrowTokenAmount - Current total borrow token amount, market's token decimals
|
|
677
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account
|
|
678
|
-
* @return {{ minDepositTokensForUtilization: BN; maxBorrowTokensForUtilization: BN }} Both
|
|
679
|
-
* scaled by the market's token decimals
|
|
680
|
-
*/
|
|
681
|
-
export function calculateTokenUtilizationLimits(
|
|
682
|
-
depositTokenAmount: BN,
|
|
683
|
-
borrowTokenAmount: BN,
|
|
684
|
-
spotMarket: SpotMarketAccount
|
|
685
|
-
): {
|
|
686
|
-
minDepositTokensForUtilization: BN;
|
|
687
|
-
maxBorrowTokensForUtilization: BN;
|
|
688
|
-
} {
|
|
689
|
-
// Calculates the allowable minimum deposit and maximum borrow amounts for immediate withdrawal based on market utilization.
|
|
690
|
-
// First, it determines a maximum withdrawal utilization from the market's target and historic utilization.
|
|
691
|
-
// Then, it deduces corresponding deposit/borrow amounts.
|
|
692
|
-
// Note: For deposit sizes below the guard threshold, withdrawals aren't blocked.
|
|
693
|
-
|
|
694
|
-
const maxWithdrawUtilization = BN.max(
|
|
695
|
-
new BN(spotMarket.optimalUtilization),
|
|
696
|
-
spotMarket.utilizationTwap.add(
|
|
697
|
-
SPOT_MARKET_UTILIZATION_PRECISION.sub(spotMarket.utilizationTwap).div(
|
|
698
|
-
new BN(2)
|
|
699
|
-
)
|
|
700
|
-
)
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
let minDepositTokensForUtilization = borrowTokenAmount
|
|
704
|
-
.mul(SPOT_MARKET_UTILIZATION_PRECISION)
|
|
705
|
-
.div(maxWithdrawUtilization);
|
|
706
|
-
|
|
707
|
-
// don't block withdraws for deposit sizes below guard threshold
|
|
708
|
-
minDepositTokensForUtilization = BN.min(
|
|
709
|
-
minDepositTokensForUtilization,
|
|
710
|
-
depositTokenAmount.sub(spotMarket.withdrawGuardThreshold)
|
|
711
|
-
);
|
|
712
|
-
|
|
713
|
-
let maxBorrowTokensForUtilization = maxWithdrawUtilization
|
|
714
|
-
.mul(depositTokenAmount)
|
|
715
|
-
.div(SPOT_MARKET_UTILIZATION_PRECISION);
|
|
716
|
-
|
|
717
|
-
maxBorrowTokensForUtilization = BN.max(
|
|
718
|
-
spotMarket.withdrawGuardThreshold,
|
|
719
|
-
maxBorrowTokensForUtilization
|
|
720
|
-
);
|
|
721
|
-
|
|
722
|
-
return {
|
|
723
|
-
minDepositTokensForUtilization,
|
|
724
|
-
maxBorrowTokensForUtilization,
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* Estimates the current immediate withdraw/borrow limits for a spot market, mirroring the
|
|
730
|
-
* on-chain `check_withdraw_limits` / `get_max_withdraw_for_market_with_token_amount` guard
|
|
731
|
-
* (combining `calculate_min_deposit_token_amount`, `calculate_max_borrow_token_amount`, and
|
|
732
|
-
* `calculateTokenUtilizationLimits`). Because the SDK cannot force an on-chain TWAP update
|
|
733
|
-
* before reading it, this projects a "live" 24h deposit/borrow TWAP by weighting the stored
|
|
734
|
-
* TWAP and the current amount by `sinceStart`/`sinceLast` (the same weighted-average shape as
|
|
735
|
-
* `update_spot_market_twap_stats`, without its rounding bias term) before deriving limits, so
|
|
736
|
-
* the result approximates what the on-chain TWAP would be if updated at `now`.
|
|
737
|
-
*
|
|
738
|
-
* Deposit/borrow TWAP friction bands differ by pool: the main pool (`poolId === 0`) targets
|
|
739
|
-
* ~30-92.5% utilization (borrow ceiling is `lesserDepositAmount` clamped between 1/3 and
|
|
740
|
-
* 13/14 of itself, floored around the live borrow TWAP + 1/5), isolated pools (`poolId !== 0`)
|
|
741
|
-
* target ~50-95% (clamped between 1/2 and 19/20, floored around the live borrow TWAP + 1/3).
|
|
742
|
-
* `lesserDepositAmount` is `min(currentDepositAmount, live deposit TWAP)` — using the smaller of
|
|
743
|
-
* the two keeps the borrow ceiling conservative whether deposits are rising or falling.
|
|
744
|
-
* `borrowLimit` is additionally zeroed for `assetTier === 'protected'` markets, and both limits
|
|
745
|
-
* are clamped by `maxTokenBorrowsFraction` of `maxTokenDeposits` when that cap is configured.
|
|
746
|
-
*
|
|
747
|
-
* @param {SpotMarketAccount} spotMarket - The spot market account
|
|
748
|
-
* @param {BN} now - The timestamp (unix seconds) to project the live TWAP up to
|
|
749
|
-
* @return {{ borrowLimit: BN; withdrawLimit: BN; minDepositAmount: BN; maxBorrowAmount: BN;
|
|
750
|
-
* currentDepositAmount: BN; currentBorrowAmount: BN }} All values scaled by the market's token
|
|
751
|
-
* decimals. `withdrawLimit`/`borrowLimit` are floored at zero (a market already past its
|
|
752
|
-
* min-deposit/max-borrow bound reports zero remaining room rather than negative)
|
|
753
|
-
*/
|
|
754
|
-
export function calculateWithdrawLimit(
|
|
755
|
-
spotMarket: SpotMarketAccount,
|
|
756
|
-
now: BN
|
|
757
|
-
): {
|
|
758
|
-
borrowLimit: BN;
|
|
759
|
-
withdrawLimit: BN;
|
|
760
|
-
minDepositAmount: BN;
|
|
761
|
-
maxBorrowAmount: BN;
|
|
762
|
-
currentDepositAmount: BN;
|
|
763
|
-
currentBorrowAmount: BN;
|
|
764
|
-
} {
|
|
765
|
-
const marketDepositTokenAmount = getTokenAmount(
|
|
766
|
-
spotMarket.depositBalance,
|
|
767
|
-
spotMarket,
|
|
768
|
-
SpotBalanceType.DEPOSIT
|
|
769
|
-
);
|
|
770
|
-
const marketBorrowTokenAmount = getTokenAmount(
|
|
771
|
-
spotMarket.borrowBalance,
|
|
772
|
-
spotMarket,
|
|
773
|
-
SpotBalanceType.BORROW
|
|
774
|
-
);
|
|
775
|
-
|
|
776
|
-
const twentyFourHours = new BN(60 * 60 * 24);
|
|
777
|
-
const sinceLast = now.sub(spotMarket.lastTwapTs);
|
|
778
|
-
const sinceStart = BN.max(ZERO, twentyFourHours.sub(sinceLast));
|
|
779
|
-
const borrowTokenTwapLive = spotMarket.borrowTokenTwap
|
|
780
|
-
.mul(sinceStart)
|
|
781
|
-
.add(marketBorrowTokenAmount.mul(sinceLast))
|
|
782
|
-
.div(sinceLast.add(sinceStart));
|
|
783
|
-
|
|
784
|
-
const depositTokenTwapLive = spotMarket.depositTokenTwap
|
|
785
|
-
.mul(sinceStart)
|
|
786
|
-
.add(marketDepositTokenAmount.mul(sinceLast))
|
|
787
|
-
.div(sinceLast.add(sinceStart));
|
|
788
|
-
|
|
789
|
-
const lesserDepositAmount = BN.min(
|
|
790
|
-
marketDepositTokenAmount,
|
|
791
|
-
depositTokenTwapLive
|
|
792
|
-
);
|
|
793
|
-
let maxBorrowTokensTwap;
|
|
794
|
-
|
|
795
|
-
if (spotMarket.poolId == 0) {
|
|
796
|
-
maxBorrowTokensTwap = BN.max(
|
|
797
|
-
spotMarket.withdrawGuardThreshold,
|
|
798
|
-
BN.min(
|
|
799
|
-
BN.max(
|
|
800
|
-
lesserDepositAmount.div(new BN(3)),
|
|
801
|
-
borrowTokenTwapLive.add(lesserDepositAmount.div(new BN(5)))
|
|
802
|
-
),
|
|
803
|
-
lesserDepositAmount.sub(lesserDepositAmount.div(new BN(14)))
|
|
804
|
-
)
|
|
805
|
-
); // main pool between ~30-92.5% utilization with friction on twap in 20% increments
|
|
806
|
-
} else {
|
|
807
|
-
maxBorrowTokensTwap = BN.max(
|
|
808
|
-
spotMarket.withdrawGuardThreshold,
|
|
809
|
-
BN.min(
|
|
810
|
-
BN.max(
|
|
811
|
-
lesserDepositAmount.div(new BN(2)),
|
|
812
|
-
borrowTokenTwapLive.add(lesserDepositAmount.div(new BN(3)))
|
|
813
|
-
),
|
|
814
|
-
lesserDepositAmount.sub(lesserDepositAmount.div(new BN(20)))
|
|
815
|
-
)
|
|
816
|
-
); // isolated pools between 50-95% utilization with friction on twap in 33% increments
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
const minDepositTokensTwap = depositTokenTwapLive.sub(
|
|
820
|
-
BN.max(
|
|
821
|
-
depositTokenTwapLive.div(new BN(4)),
|
|
822
|
-
BN.min(spotMarket.withdrawGuardThreshold, depositTokenTwapLive)
|
|
823
|
-
)
|
|
824
|
-
);
|
|
825
|
-
|
|
826
|
-
const { minDepositTokensForUtilization, maxBorrowTokensForUtilization } =
|
|
827
|
-
calculateTokenUtilizationLimits(
|
|
828
|
-
marketDepositTokenAmount,
|
|
829
|
-
marketBorrowTokenAmount,
|
|
830
|
-
spotMarket
|
|
831
|
-
);
|
|
832
|
-
|
|
833
|
-
const minDepositTokens = BN.max(
|
|
834
|
-
minDepositTokensForUtilization,
|
|
835
|
-
minDepositTokensTwap
|
|
836
|
-
);
|
|
837
|
-
|
|
838
|
-
let maxBorrowTokens = BN.min(
|
|
839
|
-
maxBorrowTokensForUtilization,
|
|
840
|
-
maxBorrowTokensTwap
|
|
841
|
-
);
|
|
842
|
-
|
|
843
|
-
const withdrawLimit = BN.max(
|
|
844
|
-
marketDepositTokenAmount.sub(minDepositTokens),
|
|
845
|
-
ZERO
|
|
846
|
-
);
|
|
847
|
-
|
|
848
|
-
let borrowLimit = maxBorrowTokens.sub(marketBorrowTokenAmount);
|
|
849
|
-
|
|
850
|
-
borrowLimit = BN.min(
|
|
851
|
-
borrowLimit,
|
|
852
|
-
marketDepositTokenAmount.sub(marketBorrowTokenAmount)
|
|
853
|
-
);
|
|
854
|
-
|
|
855
|
-
if (spotMarket.maxTokenBorrowsFraction > 0) {
|
|
856
|
-
const maxTokenBorrowsByFraction = spotMarket.maxTokenDeposits
|
|
857
|
-
.mul(new BN(spotMarket.maxTokenBorrowsFraction))
|
|
858
|
-
.divn(10000);
|
|
859
|
-
|
|
860
|
-
const trueMaxBorrowTokensAvailable = maxTokenBorrowsByFraction.sub(
|
|
861
|
-
marketBorrowTokenAmount
|
|
862
|
-
);
|
|
863
|
-
|
|
864
|
-
maxBorrowTokens = BN.min(maxBorrowTokens, trueMaxBorrowTokensAvailable);
|
|
865
|
-
|
|
866
|
-
borrowLimit = BN.min(borrowLimit, maxBorrowTokens);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (withdrawLimit.eq(ZERO) || isVariant(spotMarket.assetTier, 'protected')) {
|
|
870
|
-
borrowLimit = ZERO;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
return {
|
|
874
|
-
borrowLimit,
|
|
875
|
-
withdrawLimit,
|
|
876
|
-
maxBorrowAmount: maxBorrowTokens,
|
|
877
|
-
minDepositAmount: minDepositTokens,
|
|
878
|
-
currentDepositAmount: marketDepositTokenAmount,
|
|
879
|
-
currentBorrowAmount: marketBorrowTokenAmount,
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/**
|
|
884
|
-
* Calculates the margin-weighted value of a spot deposit, mirroring the asset-side of the
|
|
885
|
-
* program's collateral valuation (`get_strict_token_value` + `get_asset_weight`). Uses the
|
|
886
|
-
* worst of the oracle's live price and its 5min TWAP (via `strictOraclePrice`) so a favorable
|
|
887
|
-
* price spike can't be used to over-value collateral.
|
|
888
|
-
*
|
|
889
|
-
* @param {BN} tokenAmount - The deposit token amount, scaled by `spotMarketAccount.decimals`
|
|
890
|
-
* @param {StrictOraclePrice} strictOraclePrice - Live oracle price + 5min TWAP, PRICE_PRECISION (1e6)
|
|
891
|
-
* @param {SpotMarketAccount} spotMarketAccount - The spot market account
|
|
892
|
-
* @param {number} maxMarginRatio - The user's custom max margin ratio (0 if unset), in
|
|
893
|
-
* `SPOT_MARKET_WEIGHT_PRECISION` (1e4) units; only applied when `marginCategory === 'Initial'`
|
|
894
|
-
* and the market isn't the quote spot market, capping the weight at
|
|
895
|
-
* `SPOT_MARKET_WEIGHT_PRECISION - maxMarginRatio`
|
|
896
|
-
* @param {MarginCategory} [marginCategory] - When omitted, returns the unweighted (100%) value
|
|
897
|
-
* @return {BN} The (optionally weighted) asset value, scaled by `PRICE_PRECISION` (1e6)
|
|
898
|
-
*/
|
|
899
|
-
export function getSpotAssetValue(
|
|
900
|
-
tokenAmount: BN,
|
|
901
|
-
strictOraclePrice: StrictOraclePrice,
|
|
902
|
-
spotMarketAccount: SpotMarketAccount,
|
|
903
|
-
maxMarginRatio: number,
|
|
904
|
-
marginCategory?: MarginCategory
|
|
905
|
-
): BN {
|
|
906
|
-
let assetValue = getStrictTokenValue(
|
|
907
|
-
tokenAmount,
|
|
908
|
-
spotMarketAccount.decimals,
|
|
909
|
-
strictOraclePrice
|
|
910
|
-
);
|
|
911
|
-
|
|
912
|
-
if (marginCategory !== undefined) {
|
|
913
|
-
let weight = calculateAssetWeight(
|
|
914
|
-
tokenAmount,
|
|
915
|
-
strictOraclePrice.current,
|
|
916
|
-
spotMarketAccount,
|
|
917
|
-
marginCategory
|
|
918
|
-
);
|
|
919
|
-
|
|
920
|
-
if (
|
|
921
|
-
marginCategory === 'Initial' &&
|
|
922
|
-
spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
923
|
-
) {
|
|
924
|
-
const userCustomAssetWeight = BN.max(
|
|
925
|
-
ZERO,
|
|
926
|
-
SPOT_MARKET_WEIGHT_PRECISION.sub(new BN(maxMarginRatio))
|
|
927
|
-
);
|
|
928
|
-
weight = BN.min(weight, userCustomAssetWeight);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
assetValue = assetValue.mul(weight).div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
return assetValue;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
/**
|
|
938
|
-
* Calculates the margin-weighted value of a spot borrow, mirroring the liability-side of the
|
|
939
|
-
* program's collateral valuation (`get_strict_token_value` + `get_liability_weight`). Uses the
|
|
940
|
-
* worst of the oracle's live price and its 5min TWAP (via `strictOraclePrice`) so a favorable
|
|
941
|
-
* price dip can't be used to under-value a liability.
|
|
942
|
-
*
|
|
943
|
-
* @param {BN} tokenAmount - The borrow token amount (positive), scaled by `spotMarketAccount.decimals`
|
|
944
|
-
* @param {StrictOraclePrice} strictOraclePrice - Live oracle price + 5min TWAP, PRICE_PRECISION (1e6)
|
|
945
|
-
* @param {SpotMarketAccount} spotMarketAccount - The spot market account
|
|
946
|
-
* @param {number} maxMarginRatio - The user's custom max margin ratio (0 if unset),
|
|
947
|
-
* `SPOT_MARKET_WEIGHT_PRECISION` (1e4) units; only applied when `marginCategory === 'Initial'`
|
|
948
|
-
* and the market isn't the quote spot market, flooring the weight at
|
|
949
|
-
* `SPOT_MARKET_WEIGHT_PRECISION + maxMarginRatio`
|
|
950
|
-
* @param {MarginCategory} [marginCategory] - When omitted, returns the unweighted (100%) value
|
|
951
|
-
* @param {BN} [liquidationBuffer] - Extra weight added on top (`SPOT_MARKET_WEIGHT_PRECISION`
|
|
952
|
-
* units) to make maintenance margin checks stricter during liquidation eligibility checks
|
|
953
|
-
* @return {BN} The (optionally weighted) liability value, scaled by `PRICE_PRECISION` (1e6)
|
|
954
|
-
*/
|
|
955
|
-
export function getSpotLiabilityValue(
|
|
956
|
-
tokenAmount: BN,
|
|
957
|
-
strictOraclePrice: StrictOraclePrice,
|
|
958
|
-
spotMarketAccount: SpotMarketAccount,
|
|
959
|
-
maxMarginRatio: number,
|
|
960
|
-
marginCategory?: MarginCategory,
|
|
961
|
-
liquidationBuffer?: BN
|
|
962
|
-
): BN {
|
|
963
|
-
let liabilityValue = getStrictTokenValue(
|
|
964
|
-
tokenAmount,
|
|
965
|
-
spotMarketAccount.decimals,
|
|
966
|
-
strictOraclePrice
|
|
967
|
-
);
|
|
968
|
-
|
|
969
|
-
if (marginCategory !== undefined) {
|
|
970
|
-
let weight = calculateLiabilityWeight(
|
|
971
|
-
tokenAmount,
|
|
972
|
-
spotMarketAccount,
|
|
973
|
-
marginCategory
|
|
974
|
-
);
|
|
975
|
-
|
|
976
|
-
if (
|
|
977
|
-
marginCategory === 'Initial' &&
|
|
978
|
-
spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
979
|
-
) {
|
|
980
|
-
weight = BN.max(
|
|
981
|
-
weight,
|
|
982
|
-
SPOT_MARKET_WEIGHT_PRECISION.add(new BN(maxMarginRatio))
|
|
983
|
-
);
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
if (liquidationBuffer !== undefined) {
|
|
987
|
-
weight = weight.add(liquidationBuffer);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
liabilityValue = liabilityValue
|
|
991
|
-
.mul(weight)
|
|
992
|
-
.div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
return liabilityValue;
|
|
996
|
-
}
|