@drift-labs/sdk 2.94.0-beta.2 → 2.94.0-beta.4
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/tx/baseTxSender.d.ts +1 -1
- package/lib/tx/baseTxSender.js +15 -9
- package/lib/tx/fastSingleTxSender.js +2 -2
- package/lib/tx/retryTxSender.js +1 -1
- package/lib/tx/whileValidTxSender.d.ts +2 -0
- package/lib/tx/whileValidTxSender.js +29 -3
- package/lib/user.d.ts +7 -1
- package/lib/user.js +38 -25
- package/package.json +1 -1
- package/src/tx/baseTxSender.ts +19 -11
- package/src/tx/fastSingleTxSender.ts +2 -2
- package/src/tx/retryTxSender.ts +1 -1
- package/src/tx/whileValidTxSender.ts +38 -4
- package/src/user.ts +87 -52
- package/tests/ci/idl.ts +9 -7
- package/tests/ci/verifyConstants.ts +30 -7
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.94.0-beta.
|
|
1
|
+
2.94.0-beta.4
|
package/lib/tx/baseTxSender.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare abstract class BaseTxSender implements TxSender {
|
|
|
47
47
|
sendToAdditionalConnections(rawTx: Buffer | Uint8Array, opts: ConfirmOptions): void;
|
|
48
48
|
addAdditionalConnection(newConnection: Connection): void;
|
|
49
49
|
getTimeoutCount(): number;
|
|
50
|
-
checkConfirmationResultForError(txSig: string, result:
|
|
50
|
+
checkConfirmationResultForError(txSig: string, result: SignatureResult): Promise<void>;
|
|
51
51
|
reportTransactionError(txSig: string): Promise<any>;
|
|
52
52
|
getTxLandRate(): number;
|
|
53
53
|
private defaultLandRateToFeeFunc;
|
package/lib/tx/baseTxSender.js
CHANGED
|
@@ -103,7 +103,7 @@ class BaseTxSender {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
async confirmTransactionWebSocket(signature, commitment) {
|
|
106
|
-
var _a;
|
|
106
|
+
var _a, _b;
|
|
107
107
|
let decodedSignature;
|
|
108
108
|
try {
|
|
109
109
|
decodedSignature = bs58_1.default.decode(signature);
|
|
@@ -150,11 +150,13 @@ class BaseTxSender {
|
|
|
150
150
|
if (response === null) {
|
|
151
151
|
if (this.confirmationStrategy === types_1.ConfirmationStrategy.Combo) {
|
|
152
152
|
try {
|
|
153
|
-
const rpcResponse = await this.connection.
|
|
154
|
-
|
|
153
|
+
const rpcResponse = await this.connection.getSignatureStatuses([
|
|
154
|
+
signature,
|
|
155
|
+
]);
|
|
156
|
+
if ((_b = (_a = rpcResponse === null || rpcResponse === void 0 ? void 0 : rpcResponse.value) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.confirmationStatus) {
|
|
155
157
|
response = {
|
|
156
158
|
context: rpcResponse.context,
|
|
157
|
-
value: { err: rpcResponse.value.err },
|
|
159
|
+
value: { err: rpcResponse.value[0].err },
|
|
158
160
|
};
|
|
159
161
|
return response;
|
|
160
162
|
}
|
|
@@ -176,10 +178,14 @@ class BaseTxSender {
|
|
|
176
178
|
const start = Date.now();
|
|
177
179
|
while (totalTime < this.timeout) {
|
|
178
180
|
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
const rpcResponse = await this.connection.getSignatureStatuses([
|
|
182
|
+
signature,
|
|
183
|
+
]);
|
|
184
|
+
const signatureResult = rpcResponse && ((_a = rpcResponse.value) === null || _a === void 0 ? void 0 : _a[0]);
|
|
185
|
+
if (rpcResponse &&
|
|
186
|
+
signatureResult &&
|
|
187
|
+
signatureResult.confirmationStatus === commitment) {
|
|
188
|
+
return { context: rpcResponse.context, value: { err: null } };
|
|
183
189
|
}
|
|
184
190
|
totalTime += backoffTime;
|
|
185
191
|
backoffTime = Math.min(backoffTime * 2, 5000);
|
|
@@ -238,7 +244,7 @@ class BaseTxSender {
|
|
|
238
244
|
return this.timeoutCount;
|
|
239
245
|
}
|
|
240
246
|
async checkConfirmationResultForError(txSig, result) {
|
|
241
|
-
if (result.
|
|
247
|
+
if (result.err) {
|
|
242
248
|
await this.reportTransactionError(txSig);
|
|
243
249
|
}
|
|
244
250
|
return;
|
|
@@ -63,14 +63,14 @@ class FastSingleTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
63
63
|
this.confirmTransaction(txid, opts.commitment).then(async (result) => {
|
|
64
64
|
var _a;
|
|
65
65
|
(_a = this.txSigCache) === null || _a === void 0 ? void 0 : _a.set(txid, true);
|
|
66
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
66
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
67
67
|
slot = result.context.slot;
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
else {
|
|
71
71
|
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
72
72
|
(_b = this.txSigCache) === null || _b === void 0 ? void 0 : _b.set(txid, true);
|
|
73
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
73
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
74
74
|
slot = result.context.slot;
|
|
75
75
|
}
|
|
76
76
|
}
|
package/lib/tx/retryTxSender.js
CHANGED
|
@@ -69,7 +69,7 @@ class RetryTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
69
69
|
try {
|
|
70
70
|
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
71
71
|
(_b = this.txSigCache) === null || _b === void 0 ? void 0 : _b.set(txid, true);
|
|
72
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
72
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
73
73
|
slot = result.context.slot;
|
|
74
74
|
// eslint-disable-next-line no-useless-catch
|
|
75
75
|
}
|
|
@@ -20,6 +20,8 @@ export declare class WhileValidTxSender extends BaseTxSender {
|
|
|
20
20
|
lastValidBlockHeight: number;
|
|
21
21
|
}>;
|
|
22
22
|
blockhashCommitment: Commitment;
|
|
23
|
+
useBlockHeightOffset: boolean;
|
|
24
|
+
private checkAndSetUseBlockHeightOffset;
|
|
23
25
|
constructor({ connection, wallet, opts, retrySleep, additionalConnections, additionalTxSenderCallbacks, blockhashCommitment, txHandler, trackTxLandRate, txLandRateLookbackWindowMinutes, landRateToFeeFunc, }: {
|
|
24
26
|
connection: Connection;
|
|
25
27
|
wallet: IWallet;
|
|
@@ -10,6 +10,25 @@ const bs58_1 = __importDefault(require("bs58"));
|
|
|
10
10
|
const DEFAULT_RETRY = 2000;
|
|
11
11
|
const VALID_BLOCK_HEIGHT_OFFSET = -150; // This is a bit of weirdness but the lastValidBlockHeight value returned from connection.getLatestBlockhash is always 300 blocks ahead of the current block, even though the transaction actually expires after 150 blocks. This accounts for that so that we can at least accuractely estimate the transaction expiry.
|
|
12
12
|
class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
|
|
13
|
+
async checkAndSetUseBlockHeightOffset() {
|
|
14
|
+
this.connection.getVersion().then((version) => {
|
|
15
|
+
const solanaCoreVersion = version['solana-core'];
|
|
16
|
+
if (!solanaCoreVersion)
|
|
17
|
+
return;
|
|
18
|
+
const majorVersion = solanaCoreVersion.split('.')[0];
|
|
19
|
+
if (!majorVersion)
|
|
20
|
+
return;
|
|
21
|
+
const parsedMajorVersion = parseInt(majorVersion);
|
|
22
|
+
if (isNaN(parsedMajorVersion))
|
|
23
|
+
return;
|
|
24
|
+
if (parsedMajorVersion >= 2) {
|
|
25
|
+
this.useBlockHeightOffset = false;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.useBlockHeightOffset = true;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
13
32
|
constructor({ connection, wallet, opts = { ...anchor_1.AnchorProvider.defaultOptions(), maxRetries: 0 }, retrySleep = DEFAULT_RETRY, additionalConnections = new Array(), additionalTxSenderCallbacks = [], blockhashCommitment = 'finalized', txHandler, trackTxLandRate, txLandRateLookbackWindowMinutes, landRateToFeeFunc, }) {
|
|
14
33
|
super({
|
|
15
34
|
connection,
|
|
@@ -24,8 +43,10 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
24
43
|
});
|
|
25
44
|
this.timoutCount = 0;
|
|
26
45
|
this.untilValid = new Map();
|
|
46
|
+
this.useBlockHeightOffset = true;
|
|
27
47
|
this.retrySleep = retrySleep;
|
|
28
48
|
this.blockhashCommitment = blockhashCommitment;
|
|
49
|
+
this.checkAndSetUseBlockHeightOffset();
|
|
29
50
|
}
|
|
30
51
|
async sleep(reference) {
|
|
31
52
|
return new Promise((resolve) => {
|
|
@@ -120,11 +141,16 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
|
|
|
120
141
|
const { blockhash, lastValidBlockHeight } = this.untilValid.get(txid);
|
|
121
142
|
const result = await this.connection.confirmTransaction({
|
|
122
143
|
signature: txid,
|
|
123
|
-
lastValidBlockHeight: lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET,
|
|
124
144
|
blockhash,
|
|
125
|
-
|
|
145
|
+
lastValidBlockHeight: this.useBlockHeightOffset
|
|
146
|
+
? lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET
|
|
147
|
+
: lastValidBlockHeight,
|
|
148
|
+
}, opts === null || opts === void 0 ? void 0 : opts.commitment);
|
|
149
|
+
if (!result) {
|
|
150
|
+
throw new Error(`Couldn't get signature status for txid: ${txid}`);
|
|
151
|
+
}
|
|
126
152
|
(_b = this.txSigCache) === null || _b === void 0 ? void 0 : _b.set(txid, true);
|
|
127
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
153
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
128
154
|
slot = result.context.slot;
|
|
129
155
|
// eslint-disable-next-line no-useless-catch
|
|
130
156
|
}
|
package/lib/user.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { PublicKey } from '@solana/web3.js';
|
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
6
6
|
import { DriftClient } from './driftClient';
|
|
7
|
-
import { HealthComponents, MarginCategory, Order, PerpMarketAccount, PerpPosition, SpotPosition, UserAccount, UserStatus } from './types';
|
|
7
|
+
import { HealthComponent, HealthComponents, MarginCategory, Order, PerpMarketAccount, PerpPosition, SpotPosition, UserAccount, UserStatus } from './types';
|
|
8
8
|
import { DataAndSlot, UserAccountEvents, UserAccountSubscriber } from './accounts/types';
|
|
9
9
|
import { BN, MarketType, PositionDirection, SpotMarketAccount } from '.';
|
|
10
10
|
import { OraclePriceData } from './oracles/types';
|
|
@@ -387,6 +387,12 @@ export declare class User {
|
|
|
387
387
|
perpTier: number;
|
|
388
388
|
spotTier: number;
|
|
389
389
|
};
|
|
390
|
+
getPerpPositionHealth({ marginCategory, perpPosition, oraclePriceData, quoteOraclePriceData, }: {
|
|
391
|
+
marginCategory: MarginCategory;
|
|
392
|
+
perpPosition: PerpPosition;
|
|
393
|
+
oraclePriceData?: OraclePriceData;
|
|
394
|
+
quoteOraclePriceData?: OraclePriceData;
|
|
395
|
+
}): HealthComponent;
|
|
390
396
|
getHealthComponents({ marginCategory, }: {
|
|
391
397
|
marginCategory: MarginCategory;
|
|
392
398
|
}): HealthComponents;
|
package/lib/user.js
CHANGED
|
@@ -1988,6 +1988,37 @@ class User {
|
|
|
1988
1988
|
spotTier: safestSpotTier,
|
|
1989
1989
|
};
|
|
1990
1990
|
}
|
|
1991
|
+
getPerpPositionHealth({ marginCategory, perpPosition, oraclePriceData, quoteOraclePriceData, }) {
|
|
1992
|
+
const settledLpPosition = this.getPerpPositionWithLPSettle(perpPosition.marketIndex, perpPosition)[0];
|
|
1993
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
1994
|
+
const _oraclePriceData = oraclePriceData ||
|
|
1995
|
+
this.driftClient.getOracleDataForPerpMarket(perpMarket.marketIndex);
|
|
1996
|
+
const oraclePrice = _oraclePriceData.price;
|
|
1997
|
+
const { worstCaseBaseAssetAmount: worstCaseBaseAmount, worstCaseLiabilityValue, } = (0, _1.calculateWorstCasePerpLiabilityValue)(settledLpPosition, perpMarket, oraclePrice);
|
|
1998
|
+
const marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(perpMarket, worstCaseBaseAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio));
|
|
1999
|
+
const _quoteOraclePriceData = quoteOraclePriceData ||
|
|
2000
|
+
this.driftClient.getOracleDataForSpotMarket(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
|
|
2001
|
+
let marginRequirement = worstCaseLiabilityValue
|
|
2002
|
+
.mul(_quoteOraclePriceData.price)
|
|
2003
|
+
.div(numericConstants_1.PRICE_PRECISION)
|
|
2004
|
+
.mul(marginRatio)
|
|
2005
|
+
.div(numericConstants_1.MARGIN_PRECISION);
|
|
2006
|
+
marginRequirement = marginRequirement.add(new _1.BN(perpPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
|
|
2007
|
+
if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
|
|
2008
|
+
marginRequirement = marginRequirement.add(_1.BN.max(numericConstants_1.QUOTE_PRECISION, oraclePrice
|
|
2009
|
+
.mul(perpMarket.amm.orderStepSize)
|
|
2010
|
+
.mul(numericConstants_1.QUOTE_PRECISION)
|
|
2011
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
2012
|
+
.div(numericConstants_1.PRICE_PRECISION)));
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
marketIndex: perpMarket.marketIndex,
|
|
2016
|
+
size: worstCaseBaseAmount,
|
|
2017
|
+
value: worstCaseLiabilityValue,
|
|
2018
|
+
weight: marginRatio,
|
|
2019
|
+
weightedValue: marginRequirement,
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
1991
2022
|
getHealthComponents({ marginCategory, }) {
|
|
1992
2023
|
const healthComponents = {
|
|
1993
2024
|
deposits: [],
|
|
@@ -1996,34 +2027,16 @@ class User {
|
|
|
1996
2027
|
perpPnl: [],
|
|
1997
2028
|
};
|
|
1998
2029
|
for (const perpPosition of this.getActivePerpPositions()) {
|
|
1999
|
-
const settledLpPosition = this.getPerpPositionWithLPSettle(perpPosition.marketIndex, perpPosition)[0];
|
|
2000
2030
|
const perpMarket = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
2001
2031
|
const oraclePriceData = this.driftClient.getOracleDataForPerpMarket(perpMarket.marketIndex);
|
|
2002
|
-
const oraclePrice = oraclePriceData.price;
|
|
2003
|
-
const { worstCaseBaseAssetAmount: worstCaseBaseAmount, worstCaseLiabilityValue, } = (0, _1.calculateWorstCasePerpLiabilityValue)(settledLpPosition, perpMarket, oraclePrice);
|
|
2004
|
-
const marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(perpMarket, worstCaseBaseAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio));
|
|
2005
|
-
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
|
|
2006
2032
|
const quoteOraclePriceData = this.driftClient.getOracleDataForSpotMarket(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
marginRequirement = marginRequirement.add(_1.BN.max(numericConstants_1.QUOTE_PRECISION, oraclePrice
|
|
2015
|
-
.mul(perpMarket.amm.orderStepSize)
|
|
2016
|
-
.mul(numericConstants_1.QUOTE_PRECISION)
|
|
2017
|
-
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
2018
|
-
.div(numericConstants_1.PRICE_PRECISION)));
|
|
2019
|
-
}
|
|
2020
|
-
healthComponents.perpPositions.push({
|
|
2021
|
-
marketIndex: perpMarket.marketIndex,
|
|
2022
|
-
size: worstCaseBaseAmount,
|
|
2023
|
-
value: worstCaseLiabilityValue,
|
|
2024
|
-
weight: marginRatio,
|
|
2025
|
-
weightedValue: marginRequirement,
|
|
2026
|
-
});
|
|
2033
|
+
healthComponents.perpPositions.push(this.getPerpPositionHealth({
|
|
2034
|
+
marginCategory,
|
|
2035
|
+
perpPosition,
|
|
2036
|
+
oraclePriceData,
|
|
2037
|
+
quoteOraclePriceData,
|
|
2038
|
+
}));
|
|
2039
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
|
|
2027
2040
|
const settledPerpPosition = this.getPerpPositionWithLPSettle(perpPosition.marketIndex, perpPosition)[0];
|
|
2028
2041
|
const positionUnrealizedPnl = (0, _1.calculatePositionPNL)(perpMarket, settledPerpPosition, true, oraclePriceData);
|
|
2029
2042
|
let pnlWeight;
|
package/package.json
CHANGED
package/src/tx/baseTxSender.ts
CHANGED
|
@@ -267,13 +267,14 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
267
267
|
if (response === null) {
|
|
268
268
|
if (this.confirmationStrategy === ConfirmationStrategy.Combo) {
|
|
269
269
|
try {
|
|
270
|
-
const rpcResponse = await this.connection.
|
|
271
|
-
signature
|
|
272
|
-
);
|
|
273
|
-
|
|
270
|
+
const rpcResponse = await this.connection.getSignatureStatuses([
|
|
271
|
+
signature,
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
if (rpcResponse?.value?.[0]?.confirmationStatus) {
|
|
274
275
|
response = {
|
|
275
276
|
context: rpcResponse.context,
|
|
276
|
-
value: { err: rpcResponse.value.err },
|
|
277
|
+
value: { err: rpcResponse.value[0].err },
|
|
277
278
|
};
|
|
278
279
|
return response;
|
|
279
280
|
}
|
|
@@ -305,11 +306,18 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
305
306
|
while (totalTime < this.timeout) {
|
|
306
307
|
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
307
308
|
|
|
308
|
-
const
|
|
309
|
-
|
|
309
|
+
const rpcResponse = await this.connection.getSignatureStatuses([
|
|
310
|
+
signature,
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
const signatureResult = rpcResponse && rpcResponse.value?.[0];
|
|
310
314
|
|
|
311
|
-
if (
|
|
312
|
-
|
|
315
|
+
if (
|
|
316
|
+
rpcResponse &&
|
|
317
|
+
signatureResult &&
|
|
318
|
+
signatureResult.confirmationStatus === commitment
|
|
319
|
+
) {
|
|
320
|
+
return { context: rpcResponse.context, value: { err: null } };
|
|
313
321
|
}
|
|
314
322
|
|
|
315
323
|
totalTime += backoffTime;
|
|
@@ -398,9 +406,9 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
398
406
|
|
|
399
407
|
public async checkConfirmationResultForError(
|
|
400
408
|
txSig: string,
|
|
401
|
-
result:
|
|
409
|
+
result: SignatureResult
|
|
402
410
|
) {
|
|
403
|
-
if (result.
|
|
411
|
+
if (result.err) {
|
|
404
412
|
await this.reportTransactionError(txSig);
|
|
405
413
|
}
|
|
406
414
|
|
|
@@ -118,14 +118,14 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
118
118
|
this.confirmTransaction(txid, opts.commitment).then(
|
|
119
119
|
async (result) => {
|
|
120
120
|
this.txSigCache?.set(txid, true);
|
|
121
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
121
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
122
122
|
slot = result.context.slot;
|
|
123
123
|
}
|
|
124
124
|
);
|
|
125
125
|
} else {
|
|
126
126
|
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
127
127
|
this.txSigCache?.set(txid, true);
|
|
128
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
128
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
129
129
|
slot = result.context.slot;
|
|
130
130
|
}
|
|
131
131
|
} catch (e) {
|
package/src/tx/retryTxSender.ts
CHANGED
|
@@ -117,7 +117,7 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
117
117
|
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
118
118
|
this.txSigCache?.set(txid, true);
|
|
119
119
|
|
|
120
|
-
await this.checkConfirmationResultForError(txid, result);
|
|
120
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
121
121
|
|
|
122
122
|
slot = result.context.slot;
|
|
123
123
|
// eslint-disable-next-line no-useless-catch
|
|
@@ -35,6 +35,30 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
35
35
|
>();
|
|
36
36
|
blockhashCommitment: Commitment;
|
|
37
37
|
|
|
38
|
+
useBlockHeightOffset = true;
|
|
39
|
+
|
|
40
|
+
private async checkAndSetUseBlockHeightOffset() {
|
|
41
|
+
this.connection.getVersion().then((version) => {
|
|
42
|
+
const solanaCoreVersion = version['solana-core'];
|
|
43
|
+
|
|
44
|
+
if (!solanaCoreVersion) return;
|
|
45
|
+
|
|
46
|
+
const majorVersion = solanaCoreVersion.split('.')[0];
|
|
47
|
+
|
|
48
|
+
if (!majorVersion) return;
|
|
49
|
+
|
|
50
|
+
const parsedMajorVersion = parseInt(majorVersion);
|
|
51
|
+
|
|
52
|
+
if (isNaN(parsedMajorVersion)) return;
|
|
53
|
+
|
|
54
|
+
if (parsedMajorVersion >= 2) {
|
|
55
|
+
this.useBlockHeightOffset = false;
|
|
56
|
+
} else {
|
|
57
|
+
this.useBlockHeightOffset = true;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
public constructor({
|
|
39
63
|
connection,
|
|
40
64
|
wallet,
|
|
@@ -73,6 +97,8 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
73
97
|
});
|
|
74
98
|
this.retrySleep = retrySleep;
|
|
75
99
|
this.blockhashCommitment = blockhashCommitment;
|
|
100
|
+
|
|
101
|
+
this.checkAndSetUseBlockHeightOffset();
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
async sleep(reference: ResolveReference): Promise<void> {
|
|
@@ -209,17 +235,25 @@ export class WhileValidTxSender extends BaseTxSender {
|
|
|
209
235
|
let slot: number;
|
|
210
236
|
try {
|
|
211
237
|
const { blockhash, lastValidBlockHeight } = this.untilValid.get(txid);
|
|
238
|
+
|
|
212
239
|
const result = await this.connection.confirmTransaction(
|
|
213
240
|
{
|
|
214
241
|
signature: txid,
|
|
215
|
-
lastValidBlockHeight:
|
|
216
|
-
lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET,
|
|
217
242
|
blockhash,
|
|
243
|
+
lastValidBlockHeight: this.useBlockHeightOffset
|
|
244
|
+
? lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET
|
|
245
|
+
: lastValidBlockHeight,
|
|
218
246
|
},
|
|
219
|
-
opts
|
|
247
|
+
opts?.commitment
|
|
220
248
|
);
|
|
249
|
+
|
|
250
|
+
if (!result) {
|
|
251
|
+
throw new Error(`Couldn't get signature status for txid: ${txid}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
221
254
|
this.txSigCache?.set(txid, true);
|
|
222
|
-
|
|
255
|
+
|
|
256
|
+
await this.checkConfirmationResultForError(txid, result.value);
|
|
223
257
|
|
|
224
258
|
slot = result.context.slot;
|
|
225
259
|
// eslint-disable-next-line no-useless-catch
|
package/src/user.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
4
4
|
import { DriftClient } from './driftClient';
|
|
5
5
|
import {
|
|
6
|
+
HealthComponent,
|
|
6
7
|
HealthComponents,
|
|
7
8
|
isVariant,
|
|
8
9
|
MarginCategory,
|
|
@@ -3701,6 +3702,82 @@ export class User {
|
|
|
3701
3702
|
};
|
|
3702
3703
|
}
|
|
3703
3704
|
|
|
3705
|
+
public getPerpPositionHealth({
|
|
3706
|
+
marginCategory,
|
|
3707
|
+
perpPosition,
|
|
3708
|
+
oraclePriceData,
|
|
3709
|
+
quoteOraclePriceData,
|
|
3710
|
+
}: {
|
|
3711
|
+
marginCategory: MarginCategory;
|
|
3712
|
+
perpPosition: PerpPosition;
|
|
3713
|
+
oraclePriceData?: OraclePriceData;
|
|
3714
|
+
quoteOraclePriceData?: OraclePriceData;
|
|
3715
|
+
}): HealthComponent {
|
|
3716
|
+
const settledLpPosition = this.getPerpPositionWithLPSettle(
|
|
3717
|
+
perpPosition.marketIndex,
|
|
3718
|
+
perpPosition
|
|
3719
|
+
)[0];
|
|
3720
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(
|
|
3721
|
+
perpPosition.marketIndex
|
|
3722
|
+
);
|
|
3723
|
+
const _oraclePriceData =
|
|
3724
|
+
oraclePriceData ||
|
|
3725
|
+
this.driftClient.getOracleDataForPerpMarket(perpMarket.marketIndex);
|
|
3726
|
+
const oraclePrice = _oraclePriceData.price;
|
|
3727
|
+
const {
|
|
3728
|
+
worstCaseBaseAssetAmount: worstCaseBaseAmount,
|
|
3729
|
+
worstCaseLiabilityValue,
|
|
3730
|
+
} = calculateWorstCasePerpLiabilityValue(
|
|
3731
|
+
settledLpPosition,
|
|
3732
|
+
perpMarket,
|
|
3733
|
+
oraclePrice
|
|
3734
|
+
);
|
|
3735
|
+
|
|
3736
|
+
const marginRatio = new BN(
|
|
3737
|
+
calculateMarketMarginRatio(
|
|
3738
|
+
perpMarket,
|
|
3739
|
+
worstCaseBaseAmount.abs(),
|
|
3740
|
+
marginCategory,
|
|
3741
|
+
this.getUserAccount().maxMarginRatio
|
|
3742
|
+
)
|
|
3743
|
+
);
|
|
3744
|
+
|
|
3745
|
+
const _quoteOraclePriceData =
|
|
3746
|
+
quoteOraclePriceData ||
|
|
3747
|
+
this.driftClient.getOracleDataForSpotMarket(QUOTE_SPOT_MARKET_INDEX);
|
|
3748
|
+
|
|
3749
|
+
let marginRequirement = worstCaseLiabilityValue
|
|
3750
|
+
.mul(_quoteOraclePriceData.price)
|
|
3751
|
+
.div(PRICE_PRECISION)
|
|
3752
|
+
.mul(marginRatio)
|
|
3753
|
+
.div(MARGIN_PRECISION);
|
|
3754
|
+
|
|
3755
|
+
marginRequirement = marginRequirement.add(
|
|
3756
|
+
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
3757
|
+
);
|
|
3758
|
+
|
|
3759
|
+
if (perpPosition.lpShares.gt(ZERO)) {
|
|
3760
|
+
marginRequirement = marginRequirement.add(
|
|
3761
|
+
BN.max(
|
|
3762
|
+
QUOTE_PRECISION,
|
|
3763
|
+
oraclePrice
|
|
3764
|
+
.mul(perpMarket.amm.orderStepSize)
|
|
3765
|
+
.mul(QUOTE_PRECISION)
|
|
3766
|
+
.div(AMM_RESERVE_PRECISION)
|
|
3767
|
+
.div(PRICE_PRECISION)
|
|
3768
|
+
)
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
return {
|
|
3773
|
+
marketIndex: perpMarket.marketIndex,
|
|
3774
|
+
size: worstCaseBaseAmount,
|
|
3775
|
+
value: worstCaseLiabilityValue,
|
|
3776
|
+
weight: marginRatio,
|
|
3777
|
+
weightedValue: marginRequirement,
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3704
3781
|
public getHealthComponents({
|
|
3705
3782
|
marginCategory,
|
|
3706
3783
|
}: {
|
|
@@ -3714,72 +3791,30 @@ export class User {
|
|
|
3714
3791
|
};
|
|
3715
3792
|
|
|
3716
3793
|
for (const perpPosition of this.getActivePerpPositions()) {
|
|
3717
|
-
const settledLpPosition = this.getPerpPositionWithLPSettle(
|
|
3718
|
-
perpPosition.marketIndex,
|
|
3719
|
-
perpPosition
|
|
3720
|
-
)[0];
|
|
3721
3794
|
const perpMarket = this.driftClient.getPerpMarketAccount(
|
|
3722
3795
|
perpPosition.marketIndex
|
|
3723
3796
|
);
|
|
3797
|
+
|
|
3724
3798
|
const oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
|
|
3725
3799
|
perpMarket.marketIndex
|
|
3726
3800
|
);
|
|
3727
|
-
|
|
3728
|
-
const
|
|
3729
|
-
|
|
3730
|
-
worstCaseLiabilityValue,
|
|
3731
|
-
} = calculateWorstCasePerpLiabilityValue(
|
|
3732
|
-
settledLpPosition,
|
|
3733
|
-
perpMarket,
|
|
3734
|
-
oraclePrice
|
|
3801
|
+
|
|
3802
|
+
const quoteOraclePriceData = this.driftClient.getOracleDataForSpotMarket(
|
|
3803
|
+
QUOTE_SPOT_MARKET_INDEX
|
|
3735
3804
|
);
|
|
3736
3805
|
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
perpMarket,
|
|
3740
|
-
worstCaseBaseAmount.abs(),
|
|
3806
|
+
healthComponents.perpPositions.push(
|
|
3807
|
+
this.getPerpPositionHealth({
|
|
3741
3808
|
marginCategory,
|
|
3742
|
-
|
|
3743
|
-
|
|
3809
|
+
perpPosition,
|
|
3810
|
+
oraclePriceData,
|
|
3811
|
+
quoteOraclePriceData,
|
|
3812
|
+
})
|
|
3744
3813
|
);
|
|
3745
3814
|
|
|
3746
3815
|
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
3747
3816
|
perpMarket.quoteSpotMarketIndex
|
|
3748
3817
|
);
|
|
3749
|
-
const quoteOraclePriceData = this.driftClient.getOracleDataForSpotMarket(
|
|
3750
|
-
QUOTE_SPOT_MARKET_INDEX
|
|
3751
|
-
);
|
|
3752
|
-
|
|
3753
|
-
let marginRequirement = worstCaseLiabilityValue
|
|
3754
|
-
.mul(quoteOraclePriceData.price)
|
|
3755
|
-
.div(PRICE_PRECISION)
|
|
3756
|
-
.mul(marginRatio)
|
|
3757
|
-
.div(MARGIN_PRECISION);
|
|
3758
|
-
|
|
3759
|
-
marginRequirement = marginRequirement.add(
|
|
3760
|
-
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
3761
|
-
);
|
|
3762
|
-
|
|
3763
|
-
if (perpPosition.lpShares.gt(ZERO)) {
|
|
3764
|
-
marginRequirement = marginRequirement.add(
|
|
3765
|
-
BN.max(
|
|
3766
|
-
QUOTE_PRECISION,
|
|
3767
|
-
oraclePrice
|
|
3768
|
-
.mul(perpMarket.amm.orderStepSize)
|
|
3769
|
-
.mul(QUOTE_PRECISION)
|
|
3770
|
-
.div(AMM_RESERVE_PRECISION)
|
|
3771
|
-
.div(PRICE_PRECISION)
|
|
3772
|
-
)
|
|
3773
|
-
);
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
healthComponents.perpPositions.push({
|
|
3777
|
-
marketIndex: perpMarket.marketIndex,
|
|
3778
|
-
size: worstCaseBaseAmount,
|
|
3779
|
-
value: worstCaseLiabilityValue,
|
|
3780
|
-
weight: marginRatio,
|
|
3781
|
-
weightedValue: marginRequirement,
|
|
3782
|
-
});
|
|
3783
3818
|
|
|
3784
3819
|
const settledPerpPosition = this.getPerpPositionWithLPSettle(
|
|
3785
3820
|
perpPosition.marketIndex,
|
package/tests/ci/idl.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DriftClient,
|
|
3
|
-
BulkAccountLoader,
|
|
4
|
-
} from '../../src';
|
|
1
|
+
import { DriftClient, BulkAccountLoader } from '../../src';
|
|
5
2
|
import { Connection, Keypair } from '@solana/web3.js';
|
|
6
3
|
import { Wallet, Program } from '@coral-xyz/anchor';
|
|
7
4
|
import dotenv from 'dotenv';
|
|
@@ -40,14 +37,19 @@ describe('Verify IDL', function () {
|
|
|
40
37
|
});
|
|
41
38
|
|
|
42
39
|
it('verify idl', async () => {
|
|
43
|
-
const idl = await Program.fetchIdl(
|
|
40
|
+
const idl = await Program.fetchIdl(
|
|
41
|
+
mainnetDriftClient.program.programId,
|
|
42
|
+
mainnetDriftClient.provider
|
|
43
|
+
);
|
|
44
44
|
|
|
45
45
|
// anchor idl init seems to strip the metadata
|
|
46
|
-
idl[
|
|
46
|
+
idl['metadata'] = {
|
|
47
|
+
address: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
|
|
48
|
+
};
|
|
47
49
|
const encodedMainnetIdl = JSON.stringify(idl);
|
|
48
50
|
|
|
49
51
|
const encodedSdkIdl = JSON.stringify(driftIDL);
|
|
50
52
|
|
|
51
53
|
assert(encodedSdkIdl === encodedMainnetIdl);
|
|
52
54
|
});
|
|
53
|
-
});
|
|
55
|
+
});
|
|
@@ -61,13 +61,14 @@ describe('Verify Constants', function () {
|
|
|
61
61
|
},
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
let lutAccounts
|
|
64
|
+
let lutAccounts: string[];
|
|
65
65
|
|
|
66
66
|
before(async () => {
|
|
67
67
|
await devnetDriftClient.subscribe();
|
|
68
68
|
await mainnetDriftClient.subscribe();
|
|
69
69
|
|
|
70
|
-
const lookupTable =
|
|
70
|
+
const lookupTable =
|
|
71
|
+
await mainnetDriftClient.fetchMarketLookupTableAccount();
|
|
71
72
|
lutAccounts = lookupTable.state.addresses.map((x) => x.toBase58());
|
|
72
73
|
});
|
|
73
74
|
|
|
@@ -113,10 +114,20 @@ describe('Verify Constants', function () {
|
|
|
113
114
|
);
|
|
114
115
|
|
|
115
116
|
const lutHasMarket = lutAccounts.includes(market.pubkey.toBase58());
|
|
116
|
-
assert(
|
|
117
|
+
assert(
|
|
118
|
+
lutHasMarket,
|
|
119
|
+
`Mainnet LUT is missing spot market ${
|
|
120
|
+
market.marketIndex
|
|
121
|
+
} pubkey ${market.pubkey.toBase58()}`
|
|
122
|
+
);
|
|
117
123
|
|
|
118
124
|
const lutHasMarketOracle = lutAccounts.includes(market.oracle.toBase58());
|
|
119
|
-
assert(
|
|
125
|
+
assert(
|
|
126
|
+
lutHasMarketOracle,
|
|
127
|
+
`Mainnet LUT is missing spot market ${
|
|
128
|
+
market.marketIndex
|
|
129
|
+
} oracle ${market.oracle.toBase58()}`
|
|
130
|
+
);
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
const perpMarkets = mainnetDriftClient.getPerpMarketAccounts();
|
|
@@ -150,10 +161,22 @@ describe('Verify Constants', function () {
|
|
|
150
161
|
);
|
|
151
162
|
|
|
152
163
|
const lutHasMarket = lutAccounts.includes(market.pubkey.toBase58());
|
|
153
|
-
assert(
|
|
164
|
+
assert(
|
|
165
|
+
lutHasMarket,
|
|
166
|
+
`Mainnet LUT is missing perp market ${
|
|
167
|
+
market.marketIndex
|
|
168
|
+
} pubkey ${market.pubkey.toBase58()}`
|
|
169
|
+
);
|
|
154
170
|
|
|
155
|
-
const lutHasMarketOracle = lutAccounts.includes(
|
|
156
|
-
|
|
171
|
+
const lutHasMarketOracle = lutAccounts.includes(
|
|
172
|
+
market.amm.oracle.toBase58()
|
|
173
|
+
);
|
|
174
|
+
assert(
|
|
175
|
+
lutHasMarketOracle,
|
|
176
|
+
`Mainnet LUT is missing perp market ${
|
|
177
|
+
market.marketIndex
|
|
178
|
+
} oracle ${market.amm.oracle.toBase58()}`
|
|
179
|
+
);
|
|
157
180
|
}
|
|
158
181
|
});
|
|
159
182
|
|