@drift-labs/sdk 2.22.0-beta.3 → 2.23.0-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/lib/driftClient.d.ts +2 -1
- package/lib/driftClient.js +101 -29
- package/lib/factory/oracleClient.js +3 -0
- package/lib/idl/drift.json +14 -3
- package/lib/oracles/pythClient.d.ts +2 -1
- package/lib/oracles/pythClient.js +18 -3
- package/lib/types.d.ts +4 -0
- package/lib/types.js +1 -0
- package/lib/user.d.ts +3 -3
- package/lib/user.js +42 -24
- package/package.json +1 -1
- package/src/assert/assert.js +9 -0
- package/src/driftClient.ts +119 -30
- package/src/factory/oracleClient.ts +4 -0
- package/src/idl/drift.json +14 -3
- package/src/oracles/pythClient.ts +38 -12
- package/src/token/index.js +38 -0
- package/src/types.ts +2 -0
- package/src/user.ts +102 -43
- package/src/util/computeUnits.js +27 -0
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/promiseTimeout.js +14 -0
- package/src/util/tps.js +27 -0
- package/tests/dlob/helpers.ts +3 -0
package/lib/driftClient.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="bn.js" />
|
|
3
3
|
import { AnchorProvider, BN, Program, ProgramAccount } from '@project-serum/anchor';
|
|
4
|
-
import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, OrderType, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount, ReferrerNameAccount, OrderTriggerCondition, PerpMarketExtendedInfo } from './types';
|
|
4
|
+
import { StateAccount, IWallet, PositionDirection, UserAccount, PerpMarketAccount, OrderParams, Order, SpotMarketAccount, SpotPosition, MakerInfo, TakerInfo, OptionalOrderParams, OrderType, ReferrerInfo, MarketType, TxParams, SerumV3FulfillmentConfigAccount, ReferrerNameAccount, OrderTriggerCondition, PerpMarketExtendedInfo, UserStatsAccount } from './types';
|
|
5
5
|
import * as anchor from '@project-serum/anchor';
|
|
6
6
|
import { Connection, PublicKey, TransactionSignature, ConfirmOptions, Transaction, TransactionInstruction, AccountMeta, Signer, AddressLookupTableAccount } from '@solana/web3.js';
|
|
7
7
|
import { TokenFaucet } from './tokenFaucet';
|
|
@@ -104,6 +104,7 @@ export declare class DriftClient {
|
|
|
104
104
|
fetchAllUserAccounts(includeIdle?: boolean): Promise<ProgramAccount<UserAccount>[]>;
|
|
105
105
|
getUserAccountsForDelegate(delegate: PublicKey): Promise<UserAccount[]>;
|
|
106
106
|
getUserAccountsForAuthority(authority: PublicKey): Promise<UserAccount[]>;
|
|
107
|
+
getReferredUserStatsAccountsByReferrer(referrer: PublicKey): Promise<UserStatsAccount[]>;
|
|
107
108
|
getReferrerNameAccountsForAuthority(authority: PublicKey): Promise<ReferrerNameAccount[]>;
|
|
108
109
|
deleteUser(subAccountId?: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
109
110
|
getUser(subAccountId?: number): User;
|
package/lib/driftClient.js
CHANGED
|
@@ -464,6 +464,18 @@ class DriftClient {
|
|
|
464
464
|
]);
|
|
465
465
|
return programAccounts.map((programAccount) => programAccount.account);
|
|
466
466
|
}
|
|
467
|
+
async getReferredUserStatsAccountsByReferrer(referrer) {
|
|
468
|
+
const programAccounts = await this.program.account.userStats.all([
|
|
469
|
+
{
|
|
470
|
+
memcmp: {
|
|
471
|
+
offset: 40,
|
|
472
|
+
/** data to match, as base-58 encoded string and limited to less than 129 bytes */
|
|
473
|
+
bytes: bs58_1.default.encode(referrer.toBuffer()),
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
]);
|
|
477
|
+
return programAccounts.map((programAccount) => programAccount.account);
|
|
478
|
+
}
|
|
467
479
|
async getReferrerNameAccountsForAuthority(authority) {
|
|
468
480
|
const programAccounts = await this.program.account.referrerName.all([
|
|
469
481
|
{
|
|
@@ -559,17 +571,30 @@ class DriftClient {
|
|
|
559
571
|
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
560
572
|
// otherwise remove from slot
|
|
561
573
|
if (slot > lastUserSlot) {
|
|
562
|
-
const
|
|
574
|
+
const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
|
|
563
575
|
perpMarketAccountMap.set(marketIndex, {
|
|
564
|
-
pubkey:
|
|
576
|
+
pubkey: perpMarketAccount.pubkey,
|
|
565
577
|
isSigner: false,
|
|
566
578
|
isWritable: false,
|
|
567
579
|
});
|
|
568
|
-
oracleAccountMap.set(
|
|
569
|
-
pubkey:
|
|
580
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
581
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
570
582
|
isSigner: false,
|
|
571
583
|
isWritable: false,
|
|
572
584
|
});
|
|
585
|
+
const spotMarketAccount = this.getSpotMarketAccount(perpMarketAccount.quoteSpotMarketIndex);
|
|
586
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
587
|
+
pubkey: spotMarketAccount.pubkey,
|
|
588
|
+
isSigner: false,
|
|
589
|
+
isWritable: false,
|
|
590
|
+
});
|
|
591
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
592
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
593
|
+
pubkey: spotMarketAccount.oracle,
|
|
594
|
+
isSigner: false,
|
|
595
|
+
isWritable: false,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
573
598
|
}
|
|
574
599
|
else {
|
|
575
600
|
this.perpMarketLastSlotCache.delete(marketIndex);
|
|
@@ -579,15 +604,15 @@ class DriftClient {
|
|
|
579
604
|
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
580
605
|
// otherwise remove from slot
|
|
581
606
|
if (slot > lastUserSlot) {
|
|
582
|
-
const
|
|
607
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
583
608
|
spotMarketAccountMap.set(marketIndex, {
|
|
584
|
-
pubkey:
|
|
609
|
+
pubkey: spotMarketAccount.pubkey,
|
|
585
610
|
isSigner: false,
|
|
586
611
|
isWritable: false,
|
|
587
612
|
});
|
|
588
|
-
if (!
|
|
589
|
-
oracleAccountMap.set(
|
|
590
|
-
pubkey:
|
|
613
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
614
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
615
|
+
pubkey: spotMarketAccount.oracle,
|
|
591
616
|
isSigner: false,
|
|
592
617
|
isWritable: false,
|
|
593
618
|
});
|
|
@@ -599,42 +624,68 @@ class DriftClient {
|
|
|
599
624
|
}
|
|
600
625
|
}
|
|
601
626
|
if (params.readablePerpMarketIndex !== undefined) {
|
|
602
|
-
const
|
|
627
|
+
const perpMarketAccount = this.getPerpMarketAccount(params.readablePerpMarketIndex);
|
|
603
628
|
perpMarketAccountMap.set(params.readablePerpMarketIndex, {
|
|
604
|
-
pubkey:
|
|
629
|
+
pubkey: perpMarketAccount.pubkey,
|
|
605
630
|
isSigner: false,
|
|
606
631
|
isWritable: false,
|
|
607
632
|
});
|
|
608
|
-
oracleAccountMap.set(
|
|
609
|
-
pubkey:
|
|
633
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
634
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
610
635
|
isSigner: false,
|
|
611
636
|
isWritable: false,
|
|
612
637
|
});
|
|
638
|
+
const spotMarketAccount = this.getSpotMarketAccount(perpMarketAccount.quoteSpotMarketIndex);
|
|
639
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
640
|
+
pubkey: spotMarketAccount.pubkey,
|
|
641
|
+
isSigner: false,
|
|
642
|
+
isWritable: false,
|
|
643
|
+
});
|
|
644
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
645
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
646
|
+
pubkey: spotMarketAccount.oracle,
|
|
647
|
+
isSigner: false,
|
|
648
|
+
isWritable: false,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (params.readableSpotMarketIndexes !== undefined) {
|
|
653
|
+
for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
|
|
654
|
+
const spotMarketAccount = this.getSpotMarketAccount(readableSpotMarketIndex);
|
|
655
|
+
spotMarketAccountMap.set(readableSpotMarketIndex, {
|
|
656
|
+
pubkey: spotMarketAccount.pubkey,
|
|
657
|
+
isSigner: false,
|
|
658
|
+
isWritable: false,
|
|
659
|
+
});
|
|
660
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
661
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
662
|
+
pubkey: spotMarketAccount.oracle,
|
|
663
|
+
isSigner: false,
|
|
664
|
+
isWritable: false,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
613
668
|
}
|
|
614
669
|
if (params.writablePerpMarketIndexes !== undefined) {
|
|
615
670
|
for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
|
|
616
|
-
const
|
|
671
|
+
const perpMarketAccount = this.getPerpMarketAccount(writablePerpMarketIndex);
|
|
617
672
|
perpMarketAccountMap.set(writablePerpMarketIndex, {
|
|
618
|
-
pubkey:
|
|
673
|
+
pubkey: perpMarketAccount.pubkey,
|
|
619
674
|
isSigner: false,
|
|
620
675
|
isWritable: true,
|
|
621
676
|
});
|
|
622
|
-
oracleAccountMap.set(
|
|
623
|
-
pubkey:
|
|
677
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
678
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
624
679
|
isSigner: false,
|
|
625
680
|
isWritable: false,
|
|
626
681
|
});
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (params.readableSpotMarketIndexes !== undefined) {
|
|
630
|
-
for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
|
|
631
|
-
const spotMarketAccount = this.getSpotMarketAccount(readableSpotMarketIndex);
|
|
632
|
-
spotMarketAccountMap.set(readableSpotMarketIndex, {
|
|
682
|
+
const spotMarketAccount = this.getSpotMarketAccount(perpMarketAccount.quoteSpotMarketIndex);
|
|
683
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
633
684
|
pubkey: spotMarketAccount.pubkey,
|
|
634
685
|
isSigner: false,
|
|
635
686
|
isWritable: false,
|
|
636
687
|
});
|
|
637
|
-
if (spotMarketAccount.
|
|
688
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
638
689
|
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
639
690
|
pubkey: spotMarketAccount.oracle,
|
|
640
691
|
isSigner: false,
|
|
@@ -688,27 +739,48 @@ class DriftClient {
|
|
|
688
739
|
}
|
|
689
740
|
if (!spotPosition.openAsks.eq(numericConstants_1.ZERO) ||
|
|
690
741
|
!spotPosition.openBids.eq(numericConstants_1.ZERO)) {
|
|
742
|
+
const quoteSpotMarket = this.getQuoteSpotMarketAccount();
|
|
691
743
|
spotMarketAccountMap.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, {
|
|
692
|
-
pubkey:
|
|
744
|
+
pubkey: quoteSpotMarket.pubkey,
|
|
693
745
|
isSigner: false,
|
|
694
746
|
isWritable: false,
|
|
695
747
|
});
|
|
748
|
+
if (!quoteSpotMarket.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
749
|
+
oracleAccountMap.set(quoteSpotMarket.oracle.toString(), {
|
|
750
|
+
pubkey: quoteSpotMarket.oracle,
|
|
751
|
+
isSigner: false,
|
|
752
|
+
isWritable: false,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
696
755
|
}
|
|
697
756
|
}
|
|
698
757
|
}
|
|
699
758
|
for (const position of userAccount.perpPositions) {
|
|
700
759
|
if (!(0, position_1.positionIsAvailable)(position)) {
|
|
701
|
-
const
|
|
760
|
+
const perpMarketAccount = this.getPerpMarketAccount(position.marketIndex);
|
|
702
761
|
perpMarketAccountMap.set(position.marketIndex, {
|
|
703
|
-
pubkey:
|
|
762
|
+
pubkey: perpMarketAccount.pubkey,
|
|
704
763
|
isWritable: false,
|
|
705
764
|
isSigner: false,
|
|
706
765
|
});
|
|
707
|
-
oracleAccountMap.set(
|
|
708
|
-
pubkey:
|
|
766
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
767
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
709
768
|
isWritable: false,
|
|
710
769
|
isSigner: false,
|
|
711
770
|
});
|
|
771
|
+
const spotMarketAccount = this.getSpotMarketAccount(perpMarketAccount.quoteSpotMarketIndex);
|
|
772
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
773
|
+
pubkey: spotMarketAccount.pubkey,
|
|
774
|
+
isSigner: false,
|
|
775
|
+
isWritable: false,
|
|
776
|
+
});
|
|
777
|
+
if (!spotMarketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
|
|
778
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
779
|
+
pubkey: spotMarketAccount.oracle,
|
|
780
|
+
isSigner: false,
|
|
781
|
+
isWritable: false,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
712
784
|
}
|
|
713
785
|
}
|
|
714
786
|
}
|
|
@@ -16,6 +16,9 @@ function getOracleClient(oracleSource, connection) {
|
|
|
16
16
|
if ((0, types_1.isVariant)(oracleSource, 'pyth1M')) {
|
|
17
17
|
return new pythClient_1.PythClient(connection, new anchor_1.BN(1000000));
|
|
18
18
|
}
|
|
19
|
+
if ((0, types_1.isVariant)(oracleSource, 'pythStableCoin')) {
|
|
20
|
+
return new pythClient_1.PythClient(connection, undefined, true);
|
|
21
|
+
}
|
|
19
22
|
// if (isVariant(oracleSource, 'switchboard')) {
|
|
20
23
|
// return new SwitchboardClient(connection);
|
|
21
24
|
// }
|
package/lib/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.23.0-beta.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -4081,12 +4081,20 @@
|
|
|
4081
4081
|
"defined": "ContractTier"
|
|
4082
4082
|
}
|
|
4083
4083
|
},
|
|
4084
|
+
{
|
|
4085
|
+
"name": "padding1",
|
|
4086
|
+
"type": "bool"
|
|
4087
|
+
},
|
|
4088
|
+
{
|
|
4089
|
+
"name": "quoteSpotMarketIndex",
|
|
4090
|
+
"type": "u16"
|
|
4091
|
+
},
|
|
4084
4092
|
{
|
|
4085
4093
|
"name": "padding",
|
|
4086
4094
|
"type": {
|
|
4087
4095
|
"array": [
|
|
4088
4096
|
"u8",
|
|
4089
|
-
|
|
4097
|
+
48
|
|
4090
4098
|
]
|
|
4091
4099
|
}
|
|
4092
4100
|
}
|
|
@@ -5119,7 +5127,7 @@
|
|
|
5119
5127
|
"fields": [
|
|
5120
5128
|
{
|
|
5121
5129
|
"name": "revenueWithdrawSinceLastSettle",
|
|
5122
|
-
"type": "
|
|
5130
|
+
"type": "u64"
|
|
5123
5131
|
},
|
|
5124
5132
|
{
|
|
5125
5133
|
"name": "maxRevenueWithdrawPerPeriod",
|
|
@@ -6383,6 +6391,9 @@
|
|
|
6383
6391
|
},
|
|
6384
6392
|
{
|
|
6385
6393
|
"name": "Pyth1M"
|
|
6394
|
+
},
|
|
6395
|
+
{
|
|
6396
|
+
"name": "PythStableCoin"
|
|
6386
6397
|
}
|
|
6387
6398
|
]
|
|
6388
6399
|
}
|
|
@@ -6,7 +6,8 @@ import { BN } from '@project-serum/anchor';
|
|
|
6
6
|
export declare class PythClient implements OracleClient {
|
|
7
7
|
private connection;
|
|
8
8
|
private multiple;
|
|
9
|
-
|
|
9
|
+
private stableCoin;
|
|
10
|
+
constructor(connection: Connection, multiple?: BN, stableCoin?: boolean);
|
|
10
11
|
getOraclePriceData(pricePublicKey: PublicKey): Promise<OraclePriceData>;
|
|
11
12
|
getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData;
|
|
12
13
|
}
|
|
@@ -5,9 +5,10 @@ const client_1 = require("@pythnetwork/client");
|
|
|
5
5
|
const anchor_1 = require("@project-serum/anchor");
|
|
6
6
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
7
7
|
class PythClient {
|
|
8
|
-
constructor(connection, multiple = numericConstants_1.ONE) {
|
|
8
|
+
constructor(connection, multiple = numericConstants_1.ONE, stableCoin = false) {
|
|
9
9
|
this.connection = connection;
|
|
10
10
|
this.multiple = multiple;
|
|
11
|
+
this.stableCoin = stableCoin;
|
|
11
12
|
}
|
|
12
13
|
async getOraclePriceData(pricePublicKey) {
|
|
13
14
|
const accountInfo = await this.connection.getAccountInfo(pricePublicKey);
|
|
@@ -15,10 +16,15 @@ class PythClient {
|
|
|
15
16
|
}
|
|
16
17
|
getOraclePriceDataFromBuffer(buffer) {
|
|
17
18
|
const priceData = (0, client_1.parsePriceData)(buffer);
|
|
19
|
+
const confidence = convertPythPrice(priceData.confidence, priceData.exponent, this.multiple);
|
|
20
|
+
let price = convertPythPrice(priceData.aggregate.price, priceData.exponent, this.multiple);
|
|
21
|
+
if (this.stableCoin) {
|
|
22
|
+
price = getStableCoinPrice(price, confidence);
|
|
23
|
+
}
|
|
18
24
|
return {
|
|
19
|
-
price
|
|
25
|
+
price,
|
|
20
26
|
slot: new anchor_1.BN(priceData.lastSlot.toString()),
|
|
21
|
-
confidence
|
|
27
|
+
confidence,
|
|
22
28
|
twap: convertPythPrice(priceData.twap.value, priceData.exponent, this.multiple),
|
|
23
29
|
twapConfidence: convertPythPrice(priceData.twac.value, priceData.exponent, this.multiple),
|
|
24
30
|
hasSufficientNumberOfDataPoints: true,
|
|
@@ -34,3 +40,12 @@ function convertPythPrice(price, exponent, multiple) {
|
|
|
34
40
|
.div(pythPrecision);
|
|
35
41
|
}
|
|
36
42
|
exports.convertPythPrice = convertPythPrice;
|
|
43
|
+
const fiveBPS = new anchor_1.BN(500);
|
|
44
|
+
function getStableCoinPrice(price, confidence) {
|
|
45
|
+
if (price.sub(numericConstants_1.QUOTE_PRECISION).abs().lt(anchor_1.BN.min(confidence, fiveBPS))) {
|
|
46
|
+
return numericConstants_1.QUOTE_PRECISION;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return price;
|
|
50
|
+
}
|
|
51
|
+
}
|
package/lib/types.d.ts
CHANGED
|
@@ -139,6 +139,9 @@ export declare class OracleSource {
|
|
|
139
139
|
static readonly QUOTE_ASSET: {
|
|
140
140
|
quoteAsset: {};
|
|
141
141
|
};
|
|
142
|
+
static readonly PYTH_STABLE_COIN: {
|
|
143
|
+
pythStableCoin: {};
|
|
144
|
+
};
|
|
142
145
|
}
|
|
143
146
|
export declare class OrderType {
|
|
144
147
|
static readonly LIMIT: {
|
|
@@ -635,6 +638,7 @@ export type PerpMarketAccount = {
|
|
|
635
638
|
quoteSettledInsurance: BN;
|
|
636
639
|
quoteMaxInsurance: BN;
|
|
637
640
|
};
|
|
641
|
+
quoteSpotMarketIndex: number;
|
|
638
642
|
};
|
|
639
643
|
export type HistoricalOracleData = {
|
|
640
644
|
lastOraclePrice: BN;
|
package/lib/types.js
CHANGED
|
@@ -82,6 +82,7 @@ OracleSource.PYTH_1K = { pyth1K: {} };
|
|
|
82
82
|
OracleSource.PYTH_1M = { pyth1M: {} };
|
|
83
83
|
// static readonly SWITCHBOARD = { switchboard: {} };
|
|
84
84
|
OracleSource.QUOTE_ASSET = { quoteAsset: {} };
|
|
85
|
+
OracleSource.PYTH_STABLE_COIN = { pythStableCoin: {} };
|
|
85
86
|
class OrderType {
|
|
86
87
|
}
|
|
87
88
|
exports.OrderType = OrderType;
|
package/lib/user.d.ts
CHANGED
|
@@ -104,7 +104,7 @@ export declare class User {
|
|
|
104
104
|
* calculates unrealized position price pnl
|
|
105
105
|
* @returns : Precision QUOTE_PRECISION
|
|
106
106
|
*/
|
|
107
|
-
getUnrealizedPNL(withFunding?: boolean, marketIndex?: number, withWeightMarginCategory?: MarginCategory): BN;
|
|
107
|
+
getUnrealizedPNL(withFunding?: boolean, marketIndex?: number, withWeightMarginCategory?: MarginCategory, strict?: boolean): BN;
|
|
108
108
|
/**
|
|
109
109
|
* calculates unrealized funding payment pnl
|
|
110
110
|
* @returns : Precision QUOTE_PRECISION
|
|
@@ -125,7 +125,7 @@ export declare class User {
|
|
|
125
125
|
* calculates TotalCollateral: collateral + unrealized pnl
|
|
126
126
|
* @returns : Precision QUOTE_PRECISION
|
|
127
127
|
*/
|
|
128
|
-
getTotalCollateral(marginCategory?: MarginCategory): BN;
|
|
128
|
+
getTotalCollateral(marginCategory?: MarginCategory, strict?: boolean): BN;
|
|
129
129
|
/**
|
|
130
130
|
* calculates User Health by comparing total collateral and maint. margin requirement
|
|
131
131
|
* @returns : number (value from [0, 100])
|
|
@@ -135,7 +135,7 @@ export declare class User {
|
|
|
135
135
|
* calculates sum of position value across all positions in margin system
|
|
136
136
|
* @returns : Precision QUOTE_PRECISION
|
|
137
137
|
*/
|
|
138
|
-
getTotalPerpPositionValue(marginCategory?: MarginCategory, liquidationBuffer?: BN, includeOpenOrders?: boolean): BN;
|
|
138
|
+
getTotalPerpPositionValue(marginCategory?: MarginCategory, liquidationBuffer?: BN, includeOpenOrders?: boolean, strict?: boolean): BN;
|
|
139
139
|
/**
|
|
140
140
|
* calculates position value in margin system
|
|
141
141
|
* @returns : Precision QUOTE_PRECISION
|
package/lib/user.js
CHANGED
|
@@ -268,7 +268,7 @@ class User {
|
|
|
268
268
|
* @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
|
|
269
269
|
*/
|
|
270
270
|
getMarginRequirement(marginCategory, liquidationBuffer, strict = false) {
|
|
271
|
-
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true, strict));
|
|
271
|
+
return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true, strict).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true, strict));
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
274
274
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
@@ -292,17 +292,31 @@ class User {
|
|
|
292
292
|
* calculates unrealized position price pnl
|
|
293
293
|
* @returns : Precision QUOTE_PRECISION
|
|
294
294
|
*/
|
|
295
|
-
getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
|
|
296
|
-
const quoteSpotMarket = this.driftClient.getQuoteSpotMarketAccount();
|
|
295
|
+
getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory, strict = false) {
|
|
297
296
|
return this.getActivePerpPositions()
|
|
298
297
|
.filter((pos) => (marketIndex ? pos.marketIndex === marketIndex : true))
|
|
299
298
|
.reduce((unrealizedPnl, perpPosition) => {
|
|
300
299
|
const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
301
300
|
const oraclePriceData = this.getOracleDataForPerpMarket(market.marketIndex);
|
|
301
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
|
|
302
|
+
const quoteOraclePriceData = this.getOracleDataForSpotMarket(market.quoteSpotMarketIndex);
|
|
302
303
|
if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
303
304
|
perpPosition = this.getSettledLPPosition(perpPosition.marketIndex)[0];
|
|
304
305
|
}
|
|
305
306
|
let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, perpPosition, withFunding, oraclePriceData);
|
|
307
|
+
let quotePrice;
|
|
308
|
+
if (strict && positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
|
|
309
|
+
quotePrice = _1.BN.min(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
|
|
310
|
+
}
|
|
311
|
+
else if (strict && positionUnrealizedPnl.lt(numericConstants_1.ZERO)) {
|
|
312
|
+
quotePrice = _1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
quotePrice = quoteOraclePriceData.price;
|
|
316
|
+
}
|
|
317
|
+
positionUnrealizedPnl = positionUnrealizedPnl
|
|
318
|
+
.mul(quotePrice)
|
|
319
|
+
.div(new _1.BN(numericConstants_1.PRICE_PRECISION));
|
|
306
320
|
if (withWeightMarginCategory !== undefined) {
|
|
307
321
|
if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
|
|
308
322
|
positionUnrealizedPnl = positionUnrealizedPnl
|
|
@@ -340,31 +354,24 @@ class User {
|
|
|
340
354
|
continue;
|
|
341
355
|
}
|
|
342
356
|
const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
357
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
343
358
|
if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX &&
|
|
344
359
|
countForQuote) {
|
|
360
|
+
const tokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), spotPosition.balanceType);
|
|
345
361
|
if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
|
|
346
|
-
const
|
|
347
|
-
let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
|
|
348
|
-
if (marginCategory === 'Initial') {
|
|
349
|
-
weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
|
|
350
|
-
}
|
|
351
|
-
const weightedTokenValue = tokenAmount
|
|
352
|
-
.mul(weight)
|
|
353
|
-
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
362
|
+
const weightedTokenValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
|
|
354
363
|
netQuoteValue = netQuoteValue.sub(weightedTokenValue);
|
|
355
|
-
continue;
|
|
356
364
|
}
|
|
357
365
|
else {
|
|
358
|
-
const
|
|
359
|
-
netQuoteValue = netQuoteValue.add(
|
|
360
|
-
continue;
|
|
366
|
+
const weightedTokenValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
|
|
367
|
+
netQuoteValue = netQuoteValue.add(weightedTokenValue);
|
|
361
368
|
}
|
|
369
|
+
continue;
|
|
362
370
|
}
|
|
363
|
-
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
364
371
|
if (!includeOpenOrders && countForBase) {
|
|
365
372
|
if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
|
|
366
|
-
const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
|
|
367
|
-
const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now);
|
|
373
|
+
const tokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), _1.SpotBalanceType.BORROW);
|
|
374
|
+
const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
|
|
368
375
|
totalLiabilityValue = totalLiabilityValue.add(liabilityValue);
|
|
369
376
|
continue;
|
|
370
377
|
}
|
|
@@ -381,7 +388,7 @@ class User {
|
|
|
381
388
|
totalAssetValue = totalAssetValue.add(baseAssetValue);
|
|
382
389
|
}
|
|
383
390
|
if (worstCaseTokenAmount.lt(numericConstants_1.ZERO) && countForBase) {
|
|
384
|
-
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount
|
|
391
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
|
|
385
392
|
totalLiabilityValue = totalLiabilityValue.add(baseLiabilityValue);
|
|
386
393
|
}
|
|
387
394
|
if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO) && countForQuote) {
|
|
@@ -416,7 +423,7 @@ class User {
|
|
|
416
423
|
}
|
|
417
424
|
getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict = false, now) {
|
|
418
425
|
let liabilityValue = null;
|
|
419
|
-
if (strict
|
|
426
|
+
if (strict) {
|
|
420
427
|
const estOracleTwap = (0, oracles_1.calculateLiveOracleTwap)(spotMarketAccount.historicalOracleData, oraclePriceData, now, numericConstants_1.FIVE_MINUTE // 5MIN
|
|
421
428
|
);
|
|
422
429
|
liabilityValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData, estOracleTwap);
|
|
@@ -444,7 +451,7 @@ class User {
|
|
|
444
451
|
}
|
|
445
452
|
getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict = false, now) {
|
|
446
453
|
let assetValue = null;
|
|
447
|
-
if (strict
|
|
454
|
+
if (strict) {
|
|
448
455
|
const estOracleTwap = (0, oracles_1.calculateLiveOracleTwap)(spotMarketAccount.historicalOracleData, oraclePriceData, now, numericConstants_1.FIVE_MINUTE // 5MIN
|
|
449
456
|
);
|
|
450
457
|
assetValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData, estOracleTwap);
|
|
@@ -474,8 +481,8 @@ class User {
|
|
|
474
481
|
* calculates TotalCollateral: collateral + unrealized pnl
|
|
475
482
|
* @returns : Precision QUOTE_PRECISION
|
|
476
483
|
*/
|
|
477
|
-
getTotalCollateral(marginCategory = 'Initial') {
|
|
478
|
-
return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
484
|
+
getTotalCollateral(marginCategory = 'Initial', strict = false) {
|
|
485
|
+
return this.getSpotMarketAssetValue(undefined, marginCategory, true, strict).add(this.getUnrealizedPNL(true, undefined, marginCategory, strict));
|
|
479
486
|
}
|
|
480
487
|
/**
|
|
481
488
|
* calculates User Health by comparing total collateral and maint. margin requirement
|
|
@@ -513,7 +520,7 @@ class User {
|
|
|
513
520
|
* calculates sum of position value across all positions in margin system
|
|
514
521
|
* @returns : Precision QUOTE_PRECISION
|
|
515
522
|
*/
|
|
516
|
-
getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
|
|
523
|
+
getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders, strict = false) {
|
|
517
524
|
return this.getActivePerpPositions().reduce((totalPerpValue, perpPosition) => {
|
|
518
525
|
const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
519
526
|
if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
@@ -551,7 +558,18 @@ class User {
|
|
|
551
558
|
if ((0, types_1.isVariant)(market.status, 'settlement')) {
|
|
552
559
|
marginRatio = numericConstants_1.ZERO;
|
|
553
560
|
}
|
|
561
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
|
|
562
|
+
const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(quoteSpotMarket.oracle).data;
|
|
563
|
+
let quotePrice;
|
|
564
|
+
if (strict) {
|
|
565
|
+
quotePrice = _1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
quotePrice = quoteOraclePriceData.price;
|
|
569
|
+
}
|
|
554
570
|
baseAssetValue = baseAssetValue
|
|
571
|
+
.mul(quotePrice)
|
|
572
|
+
.div(numericConstants_1.PRICE_PRECISION)
|
|
555
573
|
.mul(marginRatio)
|
|
556
574
|
.div(numericConstants_1.MARGIN_PRECISION);
|
|
557
575
|
if (includeOpenOrders) {
|
package/package.json
CHANGED