@drift-labs/sdk 2.38.1-beta.8 → 2.39.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/adminClient.d.ts +1 -0
- package/lib/adminClient.js +11 -0
- package/lib/driftClient.d.ts +22 -2
- package/lib/driftClient.js +94 -13
- package/lib/idl/drift.json +37 -2
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/math/spotBalance.d.ts +6 -5
- package/lib/math/spotBalance.js +29 -12
- package/lib/math/spotMarket.d.ts +1 -1
- package/lib/math/spotMarket.js +2 -2
- package/lib/math/spotPosition.d.ts +16 -3
- package/lib/math/spotPosition.js +53 -9
- package/lib/oracles/strictOraclePrice.d.ts +8 -0
- package/lib/oracles/strictOraclePrice.js +17 -0
- package/lib/tx/priorityFeeCalculator.d.ts +44 -0
- package/lib/tx/priorityFeeCalculator.js +85 -0
- package/lib/types.d.ts +1 -0
- package/lib/user.d.ts +5 -4
- package/lib/user.js +71 -105
- package/package.json +1 -1
- package/src/adminClient.ts +23 -0
- package/src/driftClient.ts +170 -13
- package/src/idl/drift.json +37 -2
- package/src/index.ts +3 -0
- package/src/math/spotBalance.ts +38 -12
- package/src/math/spotMarket.ts +7 -1
- package/src/math/spotPosition.ts +133 -18
- package/src/oracles/strictOraclePrice.ts +19 -0
- package/src/tx/priorityFeeCalculator.ts +117 -0
- package/src/types.ts +1 -0
- package/src/user.ts +171 -228
- package/tests/dlob/helpers.ts +10 -7
- package/tests/tx/priorityFeeCalculator.ts +77 -0
- package/tests/user/test.ts +77 -4
package/src/driftClient.ts
CHANGED
|
@@ -113,7 +113,12 @@ import { isSpotPositionAvailable } from './math/spotPosition';
|
|
|
113
113
|
import { calculateMarketMaxAvailableInsurance } from './math/market';
|
|
114
114
|
import { fetchUserStatsAccount } from './accounts/fetch';
|
|
115
115
|
import { castNumberToSpotPrecision } from './math/spotMarket';
|
|
116
|
-
import {
|
|
116
|
+
import {
|
|
117
|
+
JupiterClient,
|
|
118
|
+
QuoteResponse,
|
|
119
|
+
Route,
|
|
120
|
+
SwapMode,
|
|
121
|
+
} from './jupiter/jupiterClient';
|
|
117
122
|
import { getNonIdleUserFilter } from './memcmp';
|
|
118
123
|
import { UserStatsSubscriptionConfig } from './userStatsConfig';
|
|
119
124
|
import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
|
|
@@ -3418,6 +3423,7 @@ export class DriftClient {
|
|
|
3418
3423
|
route,
|
|
3419
3424
|
reduceOnly,
|
|
3420
3425
|
txParams,
|
|
3426
|
+
v6,
|
|
3421
3427
|
}: {
|
|
3422
3428
|
jupiterClient: JupiterClient;
|
|
3423
3429
|
outMarketIndex: number;
|
|
@@ -3430,19 +3436,44 @@ export class DriftClient {
|
|
|
3430
3436
|
route?: Route;
|
|
3431
3437
|
reduceOnly?: SwapReduceOnly;
|
|
3432
3438
|
txParams?: TxParams;
|
|
3439
|
+
v6?: {
|
|
3440
|
+
quote?: QuoteResponse;
|
|
3441
|
+
};
|
|
3433
3442
|
}): Promise<TransactionSignature> {
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3443
|
+
let ixs: anchor.web3.TransactionInstruction[];
|
|
3444
|
+
let lookupTables: anchor.web3.AddressLookupTableAccount[];
|
|
3445
|
+
|
|
3446
|
+
if (v6) {
|
|
3447
|
+
const res = await this.getJupiterSwapIxV6({
|
|
3448
|
+
jupiterClient,
|
|
3449
|
+
outMarketIndex,
|
|
3450
|
+
inMarketIndex,
|
|
3451
|
+
outAssociatedTokenAccount,
|
|
3452
|
+
inAssociatedTokenAccount,
|
|
3453
|
+
amount,
|
|
3454
|
+
slippageBps,
|
|
3455
|
+
swapMode,
|
|
3456
|
+
quote: v6.quote,
|
|
3457
|
+
reduceOnly,
|
|
3458
|
+
});
|
|
3459
|
+
ixs = res.ixs;
|
|
3460
|
+
lookupTables = res.lookupTables;
|
|
3461
|
+
} else {
|
|
3462
|
+
const res = await this.getJupiterSwapIx({
|
|
3463
|
+
jupiterClient,
|
|
3464
|
+
outMarketIndex,
|
|
3465
|
+
inMarketIndex,
|
|
3466
|
+
outAssociatedTokenAccount,
|
|
3467
|
+
inAssociatedTokenAccount,
|
|
3468
|
+
amount,
|
|
3469
|
+
slippageBps,
|
|
3470
|
+
swapMode,
|
|
3471
|
+
route,
|
|
3472
|
+
reduceOnly,
|
|
3473
|
+
});
|
|
3474
|
+
ixs = res.ixs;
|
|
3475
|
+
lookupTables = res.lookupTables;
|
|
3476
|
+
}
|
|
3446
3477
|
|
|
3447
3478
|
const tx = (await this.buildTransaction(
|
|
3448
3479
|
ixs,
|
|
@@ -3588,6 +3619,132 @@ export class DriftClient {
|
|
|
3588
3619
|
return { ixs, lookupTables };
|
|
3589
3620
|
}
|
|
3590
3621
|
|
|
3622
|
+
public async getJupiterSwapIxV6({
|
|
3623
|
+
jupiterClient,
|
|
3624
|
+
outMarketIndex,
|
|
3625
|
+
inMarketIndex,
|
|
3626
|
+
outAssociatedTokenAccount,
|
|
3627
|
+
inAssociatedTokenAccount,
|
|
3628
|
+
amount,
|
|
3629
|
+
slippageBps,
|
|
3630
|
+
swapMode,
|
|
3631
|
+
onlyDirectRoutes,
|
|
3632
|
+
quote,
|
|
3633
|
+
reduceOnly,
|
|
3634
|
+
userAccountPublicKey,
|
|
3635
|
+
}: {
|
|
3636
|
+
jupiterClient: JupiterClient;
|
|
3637
|
+
outMarketIndex: number;
|
|
3638
|
+
inMarketIndex: number;
|
|
3639
|
+
outAssociatedTokenAccount?: PublicKey;
|
|
3640
|
+
inAssociatedTokenAccount?: PublicKey;
|
|
3641
|
+
amount: BN;
|
|
3642
|
+
slippageBps?: number;
|
|
3643
|
+
swapMode?: SwapMode;
|
|
3644
|
+
onlyDirectRoutes?: boolean;
|
|
3645
|
+
quote?: QuoteResponse;
|
|
3646
|
+
reduceOnly?: SwapReduceOnly;
|
|
3647
|
+
userAccountPublicKey?: PublicKey;
|
|
3648
|
+
}): Promise<{
|
|
3649
|
+
ixs: TransactionInstruction[];
|
|
3650
|
+
lookupTables: AddressLookupTableAccount[];
|
|
3651
|
+
}> {
|
|
3652
|
+
const outMarket = this.getSpotMarketAccount(outMarketIndex);
|
|
3653
|
+
const inMarket = this.getSpotMarketAccount(inMarketIndex);
|
|
3654
|
+
|
|
3655
|
+
if (!quote) {
|
|
3656
|
+
const fetchedQuote = await jupiterClient.getQuote({
|
|
3657
|
+
inputMint: inMarket.mint,
|
|
3658
|
+
outputMint: outMarket.mint,
|
|
3659
|
+
amount,
|
|
3660
|
+
slippageBps,
|
|
3661
|
+
swapMode,
|
|
3662
|
+
onlyDirectRoutes,
|
|
3663
|
+
});
|
|
3664
|
+
|
|
3665
|
+
quote = fetchedQuote;
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
const transaction = await jupiterClient.getSwap({
|
|
3669
|
+
quote,
|
|
3670
|
+
userPublicKey: this.provider.wallet.publicKey,
|
|
3671
|
+
slippageBps,
|
|
3672
|
+
});
|
|
3673
|
+
|
|
3674
|
+
const { transactionMessage, lookupTables } =
|
|
3675
|
+
await jupiterClient.getTransactionMessageAndLookupTables({
|
|
3676
|
+
transaction,
|
|
3677
|
+
});
|
|
3678
|
+
|
|
3679
|
+
const jupiterInstructions = jupiterClient.getJupiterInstructions({
|
|
3680
|
+
transactionMessage,
|
|
3681
|
+
inputMint: inMarket.mint,
|
|
3682
|
+
outputMint: outMarket.mint,
|
|
3683
|
+
});
|
|
3684
|
+
|
|
3685
|
+
const preInstructions = [];
|
|
3686
|
+
if (!outAssociatedTokenAccount) {
|
|
3687
|
+
outAssociatedTokenAccount = await this.getAssociatedTokenAccount(
|
|
3688
|
+
outMarket.marketIndex,
|
|
3689
|
+
false
|
|
3690
|
+
);
|
|
3691
|
+
|
|
3692
|
+
const accountInfo = await this.connection.getAccountInfo(
|
|
3693
|
+
outAssociatedTokenAccount
|
|
3694
|
+
);
|
|
3695
|
+
if (!accountInfo) {
|
|
3696
|
+
preInstructions.push(
|
|
3697
|
+
this.createAssociatedTokenAccountIdempotentInstruction(
|
|
3698
|
+
outAssociatedTokenAccount,
|
|
3699
|
+
this.provider.wallet.publicKey,
|
|
3700
|
+
this.provider.wallet.publicKey,
|
|
3701
|
+
outMarket.mint
|
|
3702
|
+
)
|
|
3703
|
+
);
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
if (!inAssociatedTokenAccount) {
|
|
3708
|
+
inAssociatedTokenAccount = await this.getAssociatedTokenAccount(
|
|
3709
|
+
inMarket.marketIndex,
|
|
3710
|
+
false
|
|
3711
|
+
);
|
|
3712
|
+
|
|
3713
|
+
const accountInfo = await this.connection.getAccountInfo(
|
|
3714
|
+
inAssociatedTokenAccount
|
|
3715
|
+
);
|
|
3716
|
+
if (!accountInfo) {
|
|
3717
|
+
preInstructions.push(
|
|
3718
|
+
this.createAssociatedTokenAccountIdempotentInstruction(
|
|
3719
|
+
inAssociatedTokenAccount,
|
|
3720
|
+
this.provider.wallet.publicKey,
|
|
3721
|
+
this.provider.wallet.publicKey,
|
|
3722
|
+
inMarket.mint
|
|
3723
|
+
)
|
|
3724
|
+
);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
const { beginSwapIx, endSwapIx } = await this.getSwapIx({
|
|
3729
|
+
outMarketIndex,
|
|
3730
|
+
inMarketIndex,
|
|
3731
|
+
amountIn: amount,
|
|
3732
|
+
inTokenAccount: inAssociatedTokenAccount,
|
|
3733
|
+
outTokenAccount: outAssociatedTokenAccount,
|
|
3734
|
+
reduceOnly,
|
|
3735
|
+
userAccountPublicKey,
|
|
3736
|
+
});
|
|
3737
|
+
|
|
3738
|
+
const ixs = [
|
|
3739
|
+
...preInstructions,
|
|
3740
|
+
beginSwapIx,
|
|
3741
|
+
...jupiterInstructions,
|
|
3742
|
+
endSwapIx,
|
|
3743
|
+
];
|
|
3744
|
+
|
|
3745
|
+
return { ixs, lookupTables };
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3591
3748
|
/**
|
|
3592
3749
|
* Get the drift begin_swap and end_swap instructions
|
|
3593
3750
|
*
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.39.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -3538,6 +3538,32 @@
|
|
|
3538
3538
|
}
|
|
3539
3539
|
]
|
|
3540
3540
|
},
|
|
3541
|
+
{
|
|
3542
|
+
"name": "updateSpotMarketScaleInitialAssetWeightStart",
|
|
3543
|
+
"accounts": [
|
|
3544
|
+
{
|
|
3545
|
+
"name": "admin",
|
|
3546
|
+
"isMut": false,
|
|
3547
|
+
"isSigner": true
|
|
3548
|
+
},
|
|
3549
|
+
{
|
|
3550
|
+
"name": "state",
|
|
3551
|
+
"isMut": false,
|
|
3552
|
+
"isSigner": false
|
|
3553
|
+
},
|
|
3554
|
+
{
|
|
3555
|
+
"name": "spotMarket",
|
|
3556
|
+
"isMut": true,
|
|
3557
|
+
"isSigner": false
|
|
3558
|
+
}
|
|
3559
|
+
],
|
|
3560
|
+
"args": [
|
|
3561
|
+
{
|
|
3562
|
+
"name": "scaleInitialAssetWeightStart",
|
|
3563
|
+
"type": "u64"
|
|
3564
|
+
}
|
|
3565
|
+
]
|
|
3566
|
+
},
|
|
3541
3567
|
{
|
|
3542
3568
|
"name": "updateSpotMarketOracle",
|
|
3543
3569
|
"accounts": [
|
|
@@ -5357,12 +5383,21 @@
|
|
|
5357
5383
|
],
|
|
5358
5384
|
"type": "u64"
|
|
5359
5385
|
},
|
|
5386
|
+
{
|
|
5387
|
+
"name": "scaleInitialAssetWeightStart",
|
|
5388
|
+
"docs": [
|
|
5389
|
+
"When to begin scaling down the initial asset weight",
|
|
5390
|
+
"disabled when 0",
|
|
5391
|
+
"precision: QUOTE_PRECISION"
|
|
5392
|
+
],
|
|
5393
|
+
"type": "u64"
|
|
5394
|
+
},
|
|
5360
5395
|
{
|
|
5361
5396
|
"name": "padding",
|
|
5362
5397
|
"type": {
|
|
5363
5398
|
"array": [
|
|
5364
5399
|
"u8",
|
|
5365
|
-
|
|
5400
|
+
48
|
|
5366
5401
|
]
|
|
5367
5402
|
}
|
|
5368
5403
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import pyth from '@pythnetwork/client';
|
|
|
5
5
|
export * from './tokenFaucet';
|
|
6
6
|
export * from './oracles/types';
|
|
7
7
|
export * from './oracles/pythClient';
|
|
8
|
+
export * from './oracles/strictOraclePrice';
|
|
8
9
|
export * from './types';
|
|
9
10
|
export * from './constants/perpMarkets';
|
|
10
11
|
export * from './accounts/fetch';
|
|
@@ -49,6 +50,7 @@ export * from './math/repeg';
|
|
|
49
50
|
export * from './math/margin';
|
|
50
51
|
export * from './math/insurance';
|
|
51
52
|
export * from './math/superStake';
|
|
53
|
+
export * from './math/spotPosition';
|
|
52
54
|
export * from './marinade';
|
|
53
55
|
export * from './orderParams';
|
|
54
56
|
export * from './slot/SlotSubscriber';
|
|
@@ -64,6 +66,7 @@ export * from './priorityFee/priorityFeeSubscriber';
|
|
|
64
66
|
export * from './phoenix/phoenixFulfillmentConfigMap';
|
|
65
67
|
export * from './tx/fastSingleTxSender';
|
|
66
68
|
export * from './tx/retryTxSender';
|
|
69
|
+
export * from './tx/priorityFeeCalculator';
|
|
67
70
|
export * from './tx/types';
|
|
68
71
|
export * from './util/computeUnits';
|
|
69
72
|
export * from './util/tps';
|
package/src/math/spotBalance.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { OraclePriceData } from '../oracles/types';
|
|
23
23
|
import { PERCENTAGE_PRECISION } from '../constants/numericConstants';
|
|
24
24
|
import { divCeil } from './utils';
|
|
25
|
+
import { StrictOraclePrice } from '../oracles/strictOraclePrice';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Calculates the balance of a given token amount including any accumulated interest. This
|
|
@@ -102,25 +103,23 @@ export function getSignedTokenAmount(
|
|
|
102
103
|
*
|
|
103
104
|
* @param {BN} tokenAmount - The amount of tokens to calculate the value for (from `getTokenAmount`)
|
|
104
105
|
* @param {number} spotDecimals - The number of decimals in the token.
|
|
105
|
-
* @param {
|
|
106
|
-
* @param {BN} oraclePriceTwap - The Time-Weighted Average Price of the oracle.
|
|
106
|
+
* @param {StrictOraclePrice} strictOraclePrice - Contains oracle price and 5min twap.
|
|
107
107
|
* @return {BN} The calculated value of the given token amount, scaled by `PRICE_PRECISION`
|
|
108
108
|
*/
|
|
109
109
|
export function getStrictTokenValue(
|
|
110
110
|
tokenAmount: BN,
|
|
111
111
|
spotDecimals: number,
|
|
112
|
-
|
|
113
|
-
oraclePriceTwap: BN
|
|
112
|
+
strictOraclePrice: StrictOraclePrice
|
|
114
113
|
): BN {
|
|
115
114
|
if (tokenAmount.eq(ZERO)) {
|
|
116
115
|
return ZERO;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
let price
|
|
120
|
-
if (tokenAmount.
|
|
121
|
-
price =
|
|
118
|
+
let price;
|
|
119
|
+
if (tokenAmount.gte(ZERO)) {
|
|
120
|
+
price = strictOraclePrice.min();
|
|
122
121
|
} else {
|
|
123
|
-
price =
|
|
122
|
+
price = strictOraclePrice.max();
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
const precisionDecrease = TEN.pow(new BN(spotDecimals));
|
|
@@ -139,7 +138,7 @@ export function getStrictTokenValue(
|
|
|
139
138
|
export function getTokenValue(
|
|
140
139
|
tokenAmount: BN,
|
|
141
140
|
spotDecimals: number,
|
|
142
|
-
oraclePriceData: OraclePriceData
|
|
141
|
+
oraclePriceData: Pick<OraclePriceData, 'price'>
|
|
143
142
|
): BN {
|
|
144
143
|
if (tokenAmount.eq(ZERO)) {
|
|
145
144
|
return ZERO;
|
|
@@ -152,6 +151,7 @@ export function getTokenValue(
|
|
|
152
151
|
|
|
153
152
|
export function calculateAssetWeight(
|
|
154
153
|
balanceAmount: BN,
|
|
154
|
+
oraclePrice: BN,
|
|
155
155
|
spotMarket: SpotMarketAccount,
|
|
156
156
|
marginCategory: MarginCategory
|
|
157
157
|
): BN {
|
|
@@ -174,7 +174,7 @@ export function calculateAssetWeight(
|
|
|
174
174
|
assetWeight = calculateSizeDiscountAssetWeight(
|
|
175
175
|
sizeInAmmReservePrecision,
|
|
176
176
|
new BN(spotMarket.imfFactor),
|
|
177
|
-
|
|
177
|
+
calculateScaledInitialAssetWeight(spotMarket, oraclePrice)
|
|
178
178
|
);
|
|
179
179
|
break;
|
|
180
180
|
case 'Maintenance':
|
|
@@ -185,13 +185,39 @@ export function calculateAssetWeight(
|
|
|
185
185
|
);
|
|
186
186
|
break;
|
|
187
187
|
default:
|
|
188
|
-
assetWeight =
|
|
188
|
+
assetWeight = calculateScaledInitialAssetWeight(spotMarket, oraclePrice);
|
|
189
189
|
break;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
return assetWeight;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
export function calculateScaledInitialAssetWeight(
|
|
196
|
+
spotMarket: SpotMarketAccount,
|
|
197
|
+
oraclePrice: BN
|
|
198
|
+
): BN {
|
|
199
|
+
if (spotMarket.scaleInitialAssetWeightStart.eq(ZERO)) {
|
|
200
|
+
return new BN(spotMarket.initialAssetWeight);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const deposits = getTokenAmount(
|
|
204
|
+
spotMarket.depositBalance,
|
|
205
|
+
spotMarket,
|
|
206
|
+
SpotBalanceType.DEPOSIT
|
|
207
|
+
);
|
|
208
|
+
const depositsValue = getTokenValue(deposits, spotMarket.decimals, {
|
|
209
|
+
price: oraclePrice,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (depositsValue.lt(spotMarket.scaleInitialAssetWeightStart)) {
|
|
213
|
+
return new BN(spotMarket.initialAssetWeight);
|
|
214
|
+
} else {
|
|
215
|
+
return new BN(spotMarket.initialAssetWeight)
|
|
216
|
+
.mul(spotMarket.scaleInitialAssetWeightStart)
|
|
217
|
+
.div(depositsValue);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
195
221
|
export function calculateLiabilityWeight(
|
|
196
222
|
size: BN,
|
|
197
223
|
spotMarket: SpotMarketAccount,
|
|
@@ -229,7 +255,7 @@ export function calculateLiabilityWeight(
|
|
|
229
255
|
);
|
|
230
256
|
break;
|
|
231
257
|
default:
|
|
232
|
-
liabilityWeight = spotMarket.initialLiabilityWeight;
|
|
258
|
+
liabilityWeight = new BN(spotMarket.initialLiabilityWeight);
|
|
233
259
|
break;
|
|
234
260
|
}
|
|
235
261
|
|
package/src/math/spotMarket.ts
CHANGED
|
@@ -21,12 +21,18 @@ export function castNumberToSpotPrecision(
|
|
|
21
21
|
|
|
22
22
|
export function calculateSpotMarketMarginRatio(
|
|
23
23
|
market: SpotMarketAccount,
|
|
24
|
+
oraclePrice: BN,
|
|
24
25
|
marginCategory: MarginCategory,
|
|
25
26
|
size: BN,
|
|
26
27
|
balanceType: SpotBalanceType
|
|
27
28
|
): number {
|
|
28
29
|
if (isVariant(balanceType, 'deposit')) {
|
|
29
|
-
const assetWeight = calculateAssetWeight(
|
|
30
|
+
const assetWeight = calculateAssetWeight(
|
|
31
|
+
size,
|
|
32
|
+
oraclePrice,
|
|
33
|
+
market,
|
|
34
|
+
marginCategory
|
|
35
|
+
);
|
|
30
36
|
return MARGIN_PRECISION.sub(assetWeight).toNumber();
|
|
31
37
|
} else {
|
|
32
38
|
const liabilityWeight = calculateLiabilityWeight(
|
package/src/math/spotPosition.ts
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
|
-
import { SpotMarketAccount, SpotPosition } from '../types';
|
|
2
|
-
import {
|
|
1
|
+
import { MarginCategory, SpotMarketAccount, SpotPosition } from '../types';
|
|
2
|
+
import {
|
|
3
|
+
SPOT_MARKET_WEIGHT_PRECISION,
|
|
4
|
+
ZERO,
|
|
5
|
+
} from '../constants/numericConstants';
|
|
3
6
|
import { BN } from '@coral-xyz/anchor';
|
|
4
7
|
import {
|
|
8
|
+
calculateAssetWeight,
|
|
9
|
+
calculateLiabilityWeight,
|
|
5
10
|
getSignedTokenAmount,
|
|
11
|
+
getStrictTokenValue,
|
|
6
12
|
getTokenAmount,
|
|
7
13
|
getTokenValue,
|
|
8
14
|
} from './spotBalance';
|
|
9
|
-
import {
|
|
15
|
+
import { StrictOraclePrice } from '../oracles/strictOraclePrice';
|
|
10
16
|
|
|
11
17
|
export function isSpotPositionAvailable(position: SpotPosition): boolean {
|
|
12
18
|
return position.scaledBalance.eq(ZERO) && position.openOrders === 0;
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
export type OrderFillSimulation = {
|
|
22
|
+
tokenAmount: BN;
|
|
23
|
+
ordersValue: BN;
|
|
24
|
+
tokenValue: BN;
|
|
25
|
+
weight: BN;
|
|
26
|
+
weightedTokenValue: BN;
|
|
27
|
+
freeCollateralContribution;
|
|
28
|
+
};
|
|
29
|
+
|
|
15
30
|
export function getWorstCaseTokenAmounts(
|
|
16
31
|
spotPosition: SpotPosition,
|
|
17
32
|
spotMarketAccount: SpotMarketAccount,
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
strictOraclePrice: StrictOraclePrice,
|
|
34
|
+
marginCategory: MarginCategory
|
|
35
|
+
): OrderFillSimulation {
|
|
20
36
|
const tokenAmount = getSignedTokenAmount(
|
|
21
37
|
getTokenAmount(
|
|
22
38
|
spotPosition.scaledBalance,
|
|
@@ -26,22 +42,121 @@ export function getWorstCaseTokenAmounts(
|
|
|
26
42
|
spotPosition.balanceType
|
|
27
43
|
);
|
|
28
44
|
|
|
29
|
-
const
|
|
30
|
-
|
|
45
|
+
const tokenValue = getStrictTokenValue(
|
|
46
|
+
tokenAmount,
|
|
47
|
+
spotMarketAccount.decimals,
|
|
48
|
+
strictOraclePrice
|
|
49
|
+
);
|
|
31
50
|
|
|
32
|
-
if (
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
if (spotPosition.openBids.eq(ZERO) && spotPosition.openAsks.eq(ZERO)) {
|
|
52
|
+
const { weight, weightedTokenValue } = calculateWeightedTokenValue(
|
|
53
|
+
tokenAmount,
|
|
54
|
+
tokenValue,
|
|
55
|
+
strictOraclePrice.current,
|
|
56
|
+
spotMarketAccount,
|
|
57
|
+
marginCategory
|
|
37
58
|
);
|
|
38
|
-
return
|
|
59
|
+
return {
|
|
60
|
+
tokenAmount,
|
|
61
|
+
ordersValue: ZERO,
|
|
62
|
+
tokenValue,
|
|
63
|
+
weight,
|
|
64
|
+
weightedTokenValue,
|
|
65
|
+
freeCollateralContribution: weightedTokenValue,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const bidsSimulation = simulateOrderFill(
|
|
70
|
+
tokenAmount,
|
|
71
|
+
tokenValue,
|
|
72
|
+
spotPosition.openBids,
|
|
73
|
+
strictOraclePrice,
|
|
74
|
+
spotMarketAccount,
|
|
75
|
+
marginCategory
|
|
76
|
+
);
|
|
77
|
+
const asksSimulation = simulateOrderFill(
|
|
78
|
+
tokenAmount,
|
|
79
|
+
tokenValue,
|
|
80
|
+
spotPosition.openAsks,
|
|
81
|
+
strictOraclePrice,
|
|
82
|
+
spotMarketAccount,
|
|
83
|
+
marginCategory
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
asksSimulation.freeCollateralContribution.lt(
|
|
88
|
+
bidsSimulation.freeCollateralContribution
|
|
89
|
+
)
|
|
90
|
+
) {
|
|
91
|
+
return asksSimulation;
|
|
39
92
|
} else {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
93
|
+
return bidsSimulation;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function calculateWeightedTokenValue(
|
|
98
|
+
tokenAmount: BN,
|
|
99
|
+
tokenValue: BN,
|
|
100
|
+
oraclePrice: BN,
|
|
101
|
+
spotMarket: SpotMarketAccount,
|
|
102
|
+
marginCategory: MarginCategory
|
|
103
|
+
): { weight: BN; weightedTokenValue: BN } {
|
|
104
|
+
let weight: BN;
|
|
105
|
+
if (tokenValue.gte(ZERO)) {
|
|
106
|
+
weight = calculateAssetWeight(
|
|
107
|
+
tokenAmount,
|
|
108
|
+
oraclePrice,
|
|
109
|
+
spotMarket,
|
|
110
|
+
marginCategory
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
weight = calculateLiabilityWeight(
|
|
114
|
+
tokenAmount.abs(),
|
|
115
|
+
spotMarket,
|
|
116
|
+
marginCategory
|
|
44
117
|
);
|
|
45
|
-
return [tokenAmountAllAsksFill, worstCaseQuoteTokenAmount];
|
|
46
118
|
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
weight: weight,
|
|
122
|
+
weightedTokenValue: tokenValue
|
|
123
|
+
.mul(weight)
|
|
124
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function simulateOrderFill(
|
|
129
|
+
tokenAmount: BN,
|
|
130
|
+
tokenValue: BN,
|
|
131
|
+
openOrders: BN,
|
|
132
|
+
strictOraclePrice: StrictOraclePrice,
|
|
133
|
+
spotMarket: SpotMarketAccount,
|
|
134
|
+
marginCategory: MarginCategory
|
|
135
|
+
): OrderFillSimulation {
|
|
136
|
+
const ordersValue = getTokenValue(openOrders.neg(), spotMarket.decimals, {
|
|
137
|
+
price: strictOraclePrice.max(),
|
|
138
|
+
});
|
|
139
|
+
const tokenAmountAfterFill = tokenAmount.add(openOrders);
|
|
140
|
+
const tokenValueAfterFill = tokenValue.add(ordersValue.neg());
|
|
141
|
+
|
|
142
|
+
const { weight, weightedTokenValue: weightedTokenValueAfterFill } =
|
|
143
|
+
calculateWeightedTokenValue(
|
|
144
|
+
tokenAmountAfterFill,
|
|
145
|
+
tokenValueAfterFill,
|
|
146
|
+
strictOraclePrice.current,
|
|
147
|
+
spotMarket,
|
|
148
|
+
marginCategory
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const freeCollateralContribution =
|
|
152
|
+
weightedTokenValueAfterFill.add(ordersValue);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
tokenAmount: tokenAmountAfterFill,
|
|
156
|
+
ordersValue: ordersValue,
|
|
157
|
+
tokenValue: tokenValueAfterFill,
|
|
158
|
+
weight,
|
|
159
|
+
weightedTokenValue: weightedTokenValueAfterFill,
|
|
160
|
+
freeCollateralContribution,
|
|
161
|
+
};
|
|
47
162
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BN } from '@coral-xyz/anchor';
|
|
2
|
+
|
|
3
|
+
export class StrictOraclePrice {
|
|
4
|
+
current: BN;
|
|
5
|
+
twap?: BN;
|
|
6
|
+
|
|
7
|
+
constructor(current: BN, twap?: BN) {
|
|
8
|
+
this.current = current;
|
|
9
|
+
this.twap = twap;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public max(): BN {
|
|
13
|
+
return this.twap ? BN.max(this.twap, this.current) : this.current;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public min(): BN {
|
|
17
|
+
return this.twap ? BN.min(this.twap, this.current) : this.current;
|
|
18
|
+
}
|
|
19
|
+
}
|