@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/src/driftClient.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
PostOnlyParams,
|
|
38
38
|
SpotBalanceType,
|
|
39
39
|
PerpMarketExtendedInfo,
|
|
40
|
+
UserStatsAccount,
|
|
40
41
|
} from './types';
|
|
41
42
|
import * as anchor from '@project-serum/anchor';
|
|
42
43
|
import driftIDL from './idl/drift.json';
|
|
@@ -776,6 +777,24 @@ export class DriftClient {
|
|
|
776
777
|
);
|
|
777
778
|
}
|
|
778
779
|
|
|
780
|
+
public async getReferredUserStatsAccountsByReferrer(
|
|
781
|
+
referrer: PublicKey
|
|
782
|
+
): Promise<UserStatsAccount[]> {
|
|
783
|
+
const programAccounts = await this.program.account.userStats.all([
|
|
784
|
+
{
|
|
785
|
+
memcmp: {
|
|
786
|
+
offset: 40,
|
|
787
|
+
/** data to match, as base-58 encoded string and limited to less than 129 bytes */
|
|
788
|
+
bytes: bs58.encode(referrer.toBuffer()),
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
]);
|
|
792
|
+
|
|
793
|
+
return programAccounts.map(
|
|
794
|
+
(programAccount) => programAccount.account as UserStatsAccount
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
|
|
779
798
|
public async getReferrerNameAccountsForAuthority(
|
|
780
799
|
authority: PublicKey
|
|
781
800
|
): Promise<ReferrerNameAccount[]> {
|
|
@@ -934,17 +953,32 @@ export class DriftClient {
|
|
|
934
953
|
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
935
954
|
// otherwise remove from slot
|
|
936
955
|
if (slot > lastUserSlot) {
|
|
937
|
-
const
|
|
956
|
+
const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
|
|
938
957
|
perpMarketAccountMap.set(marketIndex, {
|
|
939
|
-
pubkey:
|
|
958
|
+
pubkey: perpMarketAccount.pubkey,
|
|
959
|
+
isSigner: false,
|
|
960
|
+
isWritable: false,
|
|
961
|
+
});
|
|
962
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
963
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
940
964
|
isSigner: false,
|
|
941
965
|
isWritable: false,
|
|
942
966
|
});
|
|
943
|
-
|
|
944
|
-
|
|
967
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
968
|
+
perpMarketAccount.quoteSpotMarketIndex
|
|
969
|
+
);
|
|
970
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
971
|
+
pubkey: spotMarketAccount.pubkey,
|
|
945
972
|
isSigner: false,
|
|
946
973
|
isWritable: false,
|
|
947
974
|
});
|
|
975
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
976
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
977
|
+
pubkey: spotMarketAccount.oracle,
|
|
978
|
+
isSigner: false,
|
|
979
|
+
isWritable: false,
|
|
980
|
+
});
|
|
981
|
+
}
|
|
948
982
|
} else {
|
|
949
983
|
this.perpMarketLastSlotCache.delete(marketIndex);
|
|
950
984
|
}
|
|
@@ -957,15 +991,15 @@ export class DriftClient {
|
|
|
957
991
|
// if cache has more recent slot than user positions account slot, add market to remaining accounts
|
|
958
992
|
// otherwise remove from slot
|
|
959
993
|
if (slot > lastUserSlot) {
|
|
960
|
-
const
|
|
994
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
961
995
|
spotMarketAccountMap.set(marketIndex, {
|
|
962
|
-
pubkey:
|
|
996
|
+
pubkey: spotMarketAccount.pubkey,
|
|
963
997
|
isSigner: false,
|
|
964
998
|
isWritable: false,
|
|
965
999
|
});
|
|
966
|
-
if (!
|
|
967
|
-
oracleAccountMap.set(
|
|
968
|
-
pubkey:
|
|
1000
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
1001
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
1002
|
+
pubkey: spotMarketAccount.oracle,
|
|
969
1003
|
isSigner: false,
|
|
970
1004
|
isWritable: false,
|
|
971
1005
|
});
|
|
@@ -977,50 +1011,80 @@ export class DriftClient {
|
|
|
977
1011
|
}
|
|
978
1012
|
|
|
979
1013
|
if (params.readablePerpMarketIndex !== undefined) {
|
|
980
|
-
const
|
|
1014
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
981
1015
|
params.readablePerpMarketIndex
|
|
982
1016
|
);
|
|
983
1017
|
perpMarketAccountMap.set(params.readablePerpMarketIndex, {
|
|
984
|
-
pubkey:
|
|
1018
|
+
pubkey: perpMarketAccount.pubkey,
|
|
1019
|
+
isSigner: false,
|
|
1020
|
+
isWritable: false,
|
|
1021
|
+
});
|
|
1022
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
1023
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
985
1024
|
isSigner: false,
|
|
986
1025
|
isWritable: false,
|
|
987
1026
|
});
|
|
988
|
-
|
|
989
|
-
|
|
1027
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
1028
|
+
perpMarketAccount.quoteSpotMarketIndex
|
|
1029
|
+
);
|
|
1030
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
1031
|
+
pubkey: spotMarketAccount.pubkey,
|
|
990
1032
|
isSigner: false,
|
|
991
1033
|
isWritable: false,
|
|
992
1034
|
});
|
|
1035
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
1036
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
1037
|
+
pubkey: spotMarketAccount.oracle,
|
|
1038
|
+
isSigner: false,
|
|
1039
|
+
isWritable: false,
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (params.readableSpotMarketIndexes !== undefined) {
|
|
1045
|
+
for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
|
|
1046
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
1047
|
+
readableSpotMarketIndex
|
|
1048
|
+
);
|
|
1049
|
+
spotMarketAccountMap.set(readableSpotMarketIndex, {
|
|
1050
|
+
pubkey: spotMarketAccount.pubkey,
|
|
1051
|
+
isSigner: false,
|
|
1052
|
+
isWritable: false,
|
|
1053
|
+
});
|
|
1054
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
1055
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
1056
|
+
pubkey: spotMarketAccount.oracle,
|
|
1057
|
+
isSigner: false,
|
|
1058
|
+
isWritable: false,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
993
1062
|
}
|
|
994
1063
|
|
|
995
1064
|
if (params.writablePerpMarketIndexes !== undefined) {
|
|
996
1065
|
for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
|
|
997
|
-
const
|
|
1066
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
998
1067
|
writablePerpMarketIndex
|
|
999
1068
|
);
|
|
1000
1069
|
perpMarketAccountMap.set(writablePerpMarketIndex, {
|
|
1001
|
-
pubkey:
|
|
1070
|
+
pubkey: perpMarketAccount.pubkey,
|
|
1002
1071
|
isSigner: false,
|
|
1003
1072
|
isWritable: true,
|
|
1004
1073
|
});
|
|
1005
|
-
oracleAccountMap.set(
|
|
1006
|
-
pubkey:
|
|
1074
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
1075
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
1007
1076
|
isSigner: false,
|
|
1008
1077
|
isWritable: false,
|
|
1009
1078
|
});
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
if (params.readableSpotMarketIndexes !== undefined) {
|
|
1014
|
-
for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
|
|
1015
1079
|
const spotMarketAccount = this.getSpotMarketAccount(
|
|
1016
|
-
|
|
1080
|
+
perpMarketAccount.quoteSpotMarketIndex
|
|
1017
1081
|
);
|
|
1018
|
-
spotMarketAccountMap.set(
|
|
1082
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
1019
1083
|
pubkey: spotMarketAccount.pubkey,
|
|
1020
1084
|
isSigner: false,
|
|
1021
1085
|
isWritable: false,
|
|
1022
1086
|
});
|
|
1023
|
-
if (spotMarketAccount.
|
|
1087
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
1024
1088
|
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
1025
1089
|
pubkey: spotMarketAccount.oracle,
|
|
1026
1090
|
isSigner: false,
|
|
@@ -1090,27 +1154,52 @@ export class DriftClient {
|
|
|
1090
1154
|
!spotPosition.openAsks.eq(ZERO) ||
|
|
1091
1155
|
!spotPosition.openBids.eq(ZERO)
|
|
1092
1156
|
) {
|
|
1157
|
+
const quoteSpotMarket = this.getQuoteSpotMarketAccount();
|
|
1093
1158
|
spotMarketAccountMap.set(QUOTE_SPOT_MARKET_INDEX, {
|
|
1094
|
-
pubkey:
|
|
1159
|
+
pubkey: quoteSpotMarket.pubkey,
|
|
1095
1160
|
isSigner: false,
|
|
1096
1161
|
isWritable: false,
|
|
1097
1162
|
});
|
|
1163
|
+
if (!quoteSpotMarket.oracle.equals(PublicKey.default)) {
|
|
1164
|
+
oracleAccountMap.set(quoteSpotMarket.oracle.toString(), {
|
|
1165
|
+
pubkey: quoteSpotMarket.oracle,
|
|
1166
|
+
isSigner: false,
|
|
1167
|
+
isWritable: false,
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1098
1170
|
}
|
|
1099
1171
|
}
|
|
1100
1172
|
}
|
|
1101
1173
|
for (const position of userAccount.perpPositions) {
|
|
1102
1174
|
if (!positionIsAvailable(position)) {
|
|
1103
|
-
const
|
|
1175
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
1176
|
+
position.marketIndex
|
|
1177
|
+
);
|
|
1104
1178
|
perpMarketAccountMap.set(position.marketIndex, {
|
|
1105
|
-
pubkey:
|
|
1179
|
+
pubkey: perpMarketAccount.pubkey,
|
|
1106
1180
|
isWritable: false,
|
|
1107
1181
|
isSigner: false,
|
|
1108
1182
|
});
|
|
1109
|
-
oracleAccountMap.set(
|
|
1110
|
-
pubkey:
|
|
1183
|
+
oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
|
|
1184
|
+
pubkey: perpMarketAccount.amm.oracle,
|
|
1111
1185
|
isWritable: false,
|
|
1112
1186
|
isSigner: false,
|
|
1113
1187
|
});
|
|
1188
|
+
const spotMarketAccount = this.getSpotMarketAccount(
|
|
1189
|
+
perpMarketAccount.quoteSpotMarketIndex
|
|
1190
|
+
);
|
|
1191
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
1192
|
+
pubkey: spotMarketAccount.pubkey,
|
|
1193
|
+
isSigner: false,
|
|
1194
|
+
isWritable: false,
|
|
1195
|
+
});
|
|
1196
|
+
if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
|
|
1197
|
+
oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
|
|
1198
|
+
pubkey: spotMarketAccount.oracle,
|
|
1199
|
+
isSigner: false,
|
|
1200
|
+
isWritable: false,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1114
1203
|
}
|
|
1115
1204
|
}
|
|
1116
1205
|
}
|
|
@@ -22,6 +22,10 @@ export function getOracleClient(
|
|
|
22
22
|
return new PythClient(connection, new BN(1000000));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
if (isVariant(oracleSource, 'pythStableCoin')) {
|
|
26
|
+
return new PythClient(connection, undefined, true);
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
// if (isVariant(oracleSource, 'switchboard')) {
|
|
26
30
|
// return new SwitchboardClient(connection);
|
|
27
31
|
// }
|
package/src/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
|
}
|
|
@@ -2,15 +2,26 @@ import { parsePriceData } from '@pythnetwork/client';
|
|
|
2
2
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { OracleClient, OraclePriceData } from './types';
|
|
4
4
|
import { BN } from '@project-serum/anchor';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ONE,
|
|
7
|
+
PRICE_PRECISION,
|
|
8
|
+
QUOTE_PRECISION,
|
|
9
|
+
TEN,
|
|
10
|
+
} from '../constants/numericConstants';
|
|
6
11
|
|
|
7
12
|
export class PythClient implements OracleClient {
|
|
8
13
|
private connection: Connection;
|
|
9
14
|
private multiple: BN;
|
|
15
|
+
private stableCoin: boolean;
|
|
10
16
|
|
|
11
|
-
public constructor(
|
|
17
|
+
public constructor(
|
|
18
|
+
connection: Connection,
|
|
19
|
+
multiple = ONE,
|
|
20
|
+
stableCoin = false
|
|
21
|
+
) {
|
|
12
22
|
this.connection = connection;
|
|
13
23
|
this.multiple = multiple;
|
|
24
|
+
this.stableCoin = stableCoin;
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
public async getOraclePriceData(
|
|
@@ -22,18 +33,24 @@ export class PythClient implements OracleClient {
|
|
|
22
33
|
|
|
23
34
|
public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
|
|
24
35
|
const priceData = parsePriceData(buffer);
|
|
36
|
+
const confidence = convertPythPrice(
|
|
37
|
+
priceData.confidence,
|
|
38
|
+
priceData.exponent,
|
|
39
|
+
this.multiple
|
|
40
|
+
);
|
|
41
|
+
let price = convertPythPrice(
|
|
42
|
+
priceData.aggregate.price,
|
|
43
|
+
priceData.exponent,
|
|
44
|
+
this.multiple
|
|
45
|
+
);
|
|
46
|
+
if (this.stableCoin) {
|
|
47
|
+
price = getStableCoinPrice(price, confidence);
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
return {
|
|
26
|
-
price
|
|
27
|
-
priceData.aggregate.price,
|
|
28
|
-
priceData.exponent,
|
|
29
|
-
this.multiple
|
|
30
|
-
),
|
|
51
|
+
price,
|
|
31
52
|
slot: new BN(priceData.lastSlot.toString()),
|
|
32
|
-
confidence
|
|
33
|
-
priceData.confidence,
|
|
34
|
-
priceData.exponent,
|
|
35
|
-
this.multiple
|
|
36
|
-
),
|
|
53
|
+
confidence,
|
|
37
54
|
twap: convertPythPrice(
|
|
38
55
|
priceData.twap.value,
|
|
39
56
|
priceData.exponent,
|
|
@@ -60,3 +77,12 @@ export function convertPythPrice(
|
|
|
60
77
|
.mul(PRICE_PRECISION)
|
|
61
78
|
.div(pythPrecision);
|
|
62
79
|
}
|
|
80
|
+
|
|
81
|
+
const fiveBPS = new BN(500);
|
|
82
|
+
function getStableCoinPrice(price: BN, confidence: BN): BN {
|
|
83
|
+
if (price.sub(QUOTE_PRECISION).abs().lt(BN.min(confidence, fiveBPS))) {
|
|
84
|
+
return QUOTE_PRECISION;
|
|
85
|
+
} else {
|
|
86
|
+
return price;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTokenAccount = void 0;
|
|
4
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
5
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
6
|
+
function parseTokenAccount(data) {
|
|
7
|
+
const accountInfo = spl_token_1.AccountLayout.decode(data);
|
|
8
|
+
accountInfo.mint = new web3_js_1.PublicKey(accountInfo.mint);
|
|
9
|
+
accountInfo.owner = new web3_js_1.PublicKey(accountInfo.owner);
|
|
10
|
+
accountInfo.amount = spl_token_1.u64.fromBuffer(accountInfo.amount);
|
|
11
|
+
if (accountInfo.delegateOption === 0) {
|
|
12
|
+
accountInfo.delegate = null;
|
|
13
|
+
// eslint-disable-next-line new-cap
|
|
14
|
+
accountInfo.delegatedAmount = new spl_token_1.u64(0);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
accountInfo.delegate = new web3_js_1.PublicKey(accountInfo.delegate);
|
|
18
|
+
accountInfo.delegatedAmount = spl_token_1.u64.fromBuffer(accountInfo.delegatedAmount);
|
|
19
|
+
}
|
|
20
|
+
accountInfo.isInitialized = accountInfo.state !== 0;
|
|
21
|
+
accountInfo.isFrozen = accountInfo.state === 2;
|
|
22
|
+
if (accountInfo.isNativeOption === 1) {
|
|
23
|
+
accountInfo.rentExemptReserve = spl_token_1.u64.fromBuffer(accountInfo.isNative);
|
|
24
|
+
accountInfo.isNative = true;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
accountInfo.rentExemptReserve = null;
|
|
28
|
+
accountInfo.isNative = false;
|
|
29
|
+
}
|
|
30
|
+
if (accountInfo.closeAuthorityOption === 0) {
|
|
31
|
+
accountInfo.closeAuthority = null;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
accountInfo.closeAuthority = new web3_js_1.PublicKey(accountInfo.closeAuthority);
|
|
35
|
+
}
|
|
36
|
+
return accountInfo;
|
|
37
|
+
}
|
|
38
|
+
exports.parseTokenAccount = parseTokenAccount;
|
package/src/types.ts
CHANGED
|
@@ -80,6 +80,7 @@ export class OracleSource {
|
|
|
80
80
|
static readonly PYTH_1M = { pyth1M: {} };
|
|
81
81
|
// static readonly SWITCHBOARD = { switchboard: {} };
|
|
82
82
|
static readonly QUOTE_ASSET = { quoteAsset: {} };
|
|
83
|
+
static readonly PYTH_STABLE_COIN = { pythStableCoin: {} };
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export class OrderType {
|
|
@@ -560,6 +561,7 @@ export type PerpMarketAccount = {
|
|
|
560
561
|
quoteSettledInsurance: BN;
|
|
561
562
|
quoteMaxInsurance: BN;
|
|
562
563
|
};
|
|
564
|
+
quoteSpotMarketIndex: number;
|
|
563
565
|
};
|
|
564
566
|
|
|
565
567
|
export type HistoricalOracleData = {
|
package/src/user.ts
CHANGED
|
@@ -424,7 +424,8 @@ export class User {
|
|
|
424
424
|
return this.getTotalPerpPositionValue(
|
|
425
425
|
marginCategory,
|
|
426
426
|
liquidationBuffer,
|
|
427
|
-
true
|
|
427
|
+
true,
|
|
428
|
+
strict
|
|
428
429
|
).add(
|
|
429
430
|
this.getSpotMarketLiabilityValue(
|
|
430
431
|
undefined,
|
|
@@ -467,9 +468,9 @@ export class User {
|
|
|
467
468
|
public getUnrealizedPNL(
|
|
468
469
|
withFunding?: boolean,
|
|
469
470
|
marketIndex?: number,
|
|
470
|
-
withWeightMarginCategory?: MarginCategory
|
|
471
|
+
withWeightMarginCategory?: MarginCategory,
|
|
472
|
+
strict = false
|
|
471
473
|
): BN {
|
|
472
|
-
const quoteSpotMarket = this.driftClient.getQuoteSpotMarketAccount();
|
|
473
474
|
return this.getActivePerpPositions()
|
|
474
475
|
.filter((pos) => (marketIndex ? pos.marketIndex === marketIndex : true))
|
|
475
476
|
.reduce((unrealizedPnl, perpPosition) => {
|
|
@@ -480,6 +481,13 @@ export class User {
|
|
|
480
481
|
market.marketIndex
|
|
481
482
|
);
|
|
482
483
|
|
|
484
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
485
|
+
market.quoteSpotMarketIndex
|
|
486
|
+
);
|
|
487
|
+
const quoteOraclePriceData = this.getOracleDataForSpotMarket(
|
|
488
|
+
market.quoteSpotMarketIndex
|
|
489
|
+
);
|
|
490
|
+
|
|
483
491
|
if (perpPosition.lpShares.gt(ZERO)) {
|
|
484
492
|
perpPosition = this.getSettledLPPosition(perpPosition.marketIndex)[0];
|
|
485
493
|
}
|
|
@@ -491,6 +499,25 @@ export class User {
|
|
|
491
499
|
oraclePriceData
|
|
492
500
|
);
|
|
493
501
|
|
|
502
|
+
let quotePrice;
|
|
503
|
+
if (strict && positionUnrealizedPnl.gt(ZERO)) {
|
|
504
|
+
quotePrice = BN.min(
|
|
505
|
+
quoteOraclePriceData.price,
|
|
506
|
+
quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
|
|
507
|
+
);
|
|
508
|
+
} else if (strict && positionUnrealizedPnl.lt(ZERO)) {
|
|
509
|
+
quotePrice = BN.max(
|
|
510
|
+
quoteOraclePriceData.price,
|
|
511
|
+
quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
|
|
512
|
+
);
|
|
513
|
+
} else {
|
|
514
|
+
quotePrice = quoteOraclePriceData.price;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
positionUnrealizedPnl = positionUnrealizedPnl
|
|
518
|
+
.mul(quotePrice)
|
|
519
|
+
.div(new BN(PRICE_PRECISION));
|
|
520
|
+
|
|
494
521
|
if (withWeightMarginCategory !== undefined) {
|
|
495
522
|
if (positionUnrealizedPnl.gt(ZERO)) {
|
|
496
523
|
positionUnrealizedPnl = positionUnrealizedPnl
|
|
@@ -558,55 +585,60 @@ export class User {
|
|
|
558
585
|
const spotMarketAccount: SpotMarketAccount =
|
|
559
586
|
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
560
587
|
|
|
588
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
589
|
+
spotPosition.marketIndex
|
|
590
|
+
);
|
|
591
|
+
|
|
561
592
|
if (
|
|
562
593
|
spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX &&
|
|
563
594
|
countForQuote
|
|
564
595
|
) {
|
|
565
|
-
|
|
566
|
-
|
|
596
|
+
const tokenAmount = getSignedTokenAmount(
|
|
597
|
+
getTokenAmount(
|
|
567
598
|
spotPosition.scaledBalance,
|
|
568
599
|
spotMarketAccount,
|
|
569
600
|
spotPosition.balanceType
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (marginCategory === 'Initial') {
|
|
574
|
-
weight = BN.max(
|
|
575
|
-
weight,
|
|
576
|
-
new BN(this.getUserAccount().maxMarginRatio)
|
|
577
|
-
);
|
|
578
|
-
}
|
|
601
|
+
),
|
|
602
|
+
spotPosition.balanceType
|
|
603
|
+
);
|
|
579
604
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
605
|
+
if (isVariant(spotPosition.balanceType, 'borrow')) {
|
|
606
|
+
const weightedTokenValue = this.getSpotLiabilityValue(
|
|
607
|
+
tokenAmount,
|
|
608
|
+
oraclePriceData,
|
|
609
|
+
spotMarketAccount,
|
|
610
|
+
marginCategory,
|
|
611
|
+
liquidationBuffer,
|
|
612
|
+
strict,
|
|
613
|
+
now
|
|
614
|
+
).abs();
|
|
583
615
|
|
|
584
616
|
netQuoteValue = netQuoteValue.sub(weightedTokenValue);
|
|
585
|
-
|
|
586
|
-
continue;
|
|
587
617
|
} else {
|
|
588
|
-
const
|
|
589
|
-
|
|
618
|
+
const weightedTokenValue = this.getSpotAssetValue(
|
|
619
|
+
tokenAmount,
|
|
620
|
+
oraclePriceData,
|
|
590
621
|
spotMarketAccount,
|
|
591
|
-
|
|
622
|
+
marginCategory,
|
|
623
|
+
strict,
|
|
624
|
+
now
|
|
592
625
|
);
|
|
593
626
|
|
|
594
|
-
netQuoteValue = netQuoteValue.add(
|
|
595
|
-
|
|
596
|
-
continue;
|
|
627
|
+
netQuoteValue = netQuoteValue.add(weightedTokenValue);
|
|
597
628
|
}
|
|
598
|
-
}
|
|
599
629
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
603
632
|
|
|
604
633
|
if (!includeOpenOrders && countForBase) {
|
|
605
634
|
if (isVariant(spotPosition.balanceType, 'borrow')) {
|
|
606
|
-
const tokenAmount =
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
635
|
+
const tokenAmount = getSignedTokenAmount(
|
|
636
|
+
getTokenAmount(
|
|
637
|
+
spotPosition.scaledBalance,
|
|
638
|
+
spotMarketAccount,
|
|
639
|
+
spotPosition.balanceType
|
|
640
|
+
),
|
|
641
|
+
SpotBalanceType.BORROW
|
|
610
642
|
);
|
|
611
643
|
const liabilityValue = this.getSpotLiabilityValue(
|
|
612
644
|
tokenAmount,
|
|
@@ -616,7 +648,7 @@ export class User {
|
|
|
616
648
|
liquidationBuffer,
|
|
617
649
|
strict,
|
|
618
650
|
now
|
|
619
|
-
);
|
|
651
|
+
).abs();
|
|
620
652
|
totalLiabilityValue = totalLiabilityValue.add(liabilityValue);
|
|
621
653
|
|
|
622
654
|
continue;
|
|
@@ -662,14 +694,14 @@ export class User {
|
|
|
662
694
|
|
|
663
695
|
if (worstCaseTokenAmount.lt(ZERO) && countForBase) {
|
|
664
696
|
const baseLiabilityValue = this.getSpotLiabilityValue(
|
|
665
|
-
worstCaseTokenAmount
|
|
697
|
+
worstCaseTokenAmount,
|
|
666
698
|
oraclePriceData,
|
|
667
699
|
spotMarketAccount,
|
|
668
700
|
marginCategory,
|
|
669
701
|
liquidationBuffer,
|
|
670
702
|
strict,
|
|
671
703
|
now
|
|
672
|
-
);
|
|
704
|
+
).abs();
|
|
673
705
|
|
|
674
706
|
totalLiabilityValue = totalLiabilityValue.add(baseLiabilityValue);
|
|
675
707
|
}
|
|
@@ -738,7 +770,7 @@ export class User {
|
|
|
738
770
|
): BN {
|
|
739
771
|
let liabilityValue = null;
|
|
740
772
|
|
|
741
|
-
if (strict
|
|
773
|
+
if (strict) {
|
|
742
774
|
const estOracleTwap = calculateLiveOracleTwap(
|
|
743
775
|
spotMarketAccount.historicalOracleData,
|
|
744
776
|
oraclePriceData,
|
|
@@ -809,7 +841,7 @@ export class User {
|
|
|
809
841
|
now?: BN
|
|
810
842
|
): BN {
|
|
811
843
|
let assetValue = null;
|
|
812
|
-
if (strict
|
|
844
|
+
if (strict) {
|
|
813
845
|
const estOracleTwap = calculateLiveOracleTwap(
|
|
814
846
|
spotMarketAccount.historicalOracleData,
|
|
815
847
|
oraclePriceData,
|
|
@@ -886,10 +918,16 @@ export class User {
|
|
|
886
918
|
* calculates TotalCollateral: collateral + unrealized pnl
|
|
887
919
|
* @returns : Precision QUOTE_PRECISION
|
|
888
920
|
*/
|
|
889
|
-
public getTotalCollateral(
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
921
|
+
public getTotalCollateral(
|
|
922
|
+
marginCategory: MarginCategory = 'Initial',
|
|
923
|
+
strict = false
|
|
924
|
+
): BN {
|
|
925
|
+
return this.getSpotMarketAssetValue(
|
|
926
|
+
undefined,
|
|
927
|
+
marginCategory,
|
|
928
|
+
true,
|
|
929
|
+
strict
|
|
930
|
+
).add(this.getUnrealizedPNL(true, undefined, marginCategory, strict));
|
|
893
931
|
}
|
|
894
932
|
|
|
895
933
|
/**
|
|
@@ -941,7 +979,8 @@ export class User {
|
|
|
941
979
|
getTotalPerpPositionValue(
|
|
942
980
|
marginCategory?: MarginCategory,
|
|
943
981
|
liquidationBuffer?: BN,
|
|
944
|
-
includeOpenOrders?: boolean
|
|
982
|
+
includeOpenOrders?: boolean,
|
|
983
|
+
strict = false
|
|
945
984
|
): BN {
|
|
946
985
|
return this.getActivePerpPositions().reduce(
|
|
947
986
|
(totalPerpValue, perpPosition) => {
|
|
@@ -1011,7 +1050,27 @@ export class User {
|
|
|
1011
1050
|
marginRatio = ZERO;
|
|
1012
1051
|
}
|
|
1013
1052
|
|
|
1053
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
1054
|
+
market.quoteSpotMarketIndex
|
|
1055
|
+
);
|
|
1056
|
+
const quoteOraclePriceData =
|
|
1057
|
+
this.driftClient.getOraclePriceDataAndSlot(
|
|
1058
|
+
quoteSpotMarket.oracle
|
|
1059
|
+
).data;
|
|
1060
|
+
|
|
1061
|
+
let quotePrice;
|
|
1062
|
+
if (strict) {
|
|
1063
|
+
quotePrice = BN.max(
|
|
1064
|
+
quoteOraclePriceData.price,
|
|
1065
|
+
quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
|
|
1066
|
+
);
|
|
1067
|
+
} else {
|
|
1068
|
+
quotePrice = quoteOraclePriceData.price;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1014
1071
|
baseAssetValue = baseAssetValue
|
|
1072
|
+
.mul(quotePrice)
|
|
1073
|
+
.div(PRICE_PRECISION)
|
|
1015
1074
|
.mul(marginRatio)
|
|
1016
1075
|
.div(MARGIN_PRECISION);
|
|
1017
1076
|
|