@drift-labs/sdk 0.2.0-master.13 → 0.2.0-master.14
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/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
- package/lib/accounts/types.d.ts +14 -1
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/pda.d.ts +1 -0
- package/lib/addresses/pda.js +8 -1
- package/lib/admin.d.ts +2 -0
- package/lib/admin.js +18 -0
- package/lib/clearingHouse.d.ts +17 -1
- package/lib/clearingHouse.js +195 -14
- package/lib/clearingHouseConfig.d.ts +1 -0
- package/lib/clearingHouseUser.d.ts +5 -0
- package/lib/clearingHouseUser.js +97 -3
- package/lib/clearingHouseUserStats.d.ts +17 -0
- package/lib/clearingHouseUserStats.js +36 -0
- package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
- package/{src/clearingHouseConfig.js → lib/clearingHouseUserStatsConfig.js} +0 -0
- package/lib/config.js +1 -1
- package/lib/idl/clearing_house.json +614 -64
- package/lib/math/bankBalance.d.ts +4 -0
- package/lib/math/bankBalance.js +23 -1
- package/lib/math/oracles.d.ts +3 -0
- package/lib/math/oracles.js +25 -5
- package/lib/math/position.js +2 -1
- package/lib/math/trade.js +2 -2
- package/lib/types.d.ts +55 -8
- package/lib/types.js +6 -0
- package/package.json +1 -1
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
- package/src/accounts/types.ts +18 -0
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/pda.ts +13 -0
- package/src/admin.ts +29 -1
- package/src/clearingHouse.ts +318 -15
- package/src/clearingHouseConfig.ts +1 -0
- package/src/clearingHouseUser.ts +113 -10
- package/src/clearingHouseUserStats.ts +53 -0
- package/src/clearingHouseUserStatsConfig.ts +18 -0
- package/src/config.ts +1 -1
- package/src/idl/clearing_house.json +614 -64
- package/src/math/bankBalance.ts +49 -0
- package/src/math/oracles.ts +42 -5
- package/src/math/position.ts +2 -1
- package/src/math/trade.ts +2 -2
- package/src/types.ts +59 -8
- package/src/accounts/bulkAccountLoader.js +0 -197
- package/src/accounts/bulkUserSubscription.js +0 -33
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
- package/src/accounts/pollingOracleSubscriber.js +0 -93
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
- package/src/accounts/pollingUserAccountSubscriber.js +0 -132
- package/src/accounts/types.js +0 -10
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -93
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/index.js +0 -69
- package/src/mockUSDCFaucet.js +0 -280
package/lib/math/bankBalance.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateInterestAccumulated = exports.calculateLiabilityWeight = exports.calculateAssetWeight = exports.getTokenAmount = exports.getBalance = void 0;
|
|
3
|
+
exports.calculateWithdrawLimit = exports.calculateInterestAccumulated = exports.calculateLiabilityWeight = exports.calculateAssetWeight = exports.getTokenAmount = exports.getBalance = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const anchor_1 = require("@project-serum/anchor");
|
|
6
6
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
@@ -126,3 +126,25 @@ function calculateInterestAccumulated(bank, now) {
|
|
|
126
126
|
return { borrowInterest, depositInterest };
|
|
127
127
|
}
|
|
128
128
|
exports.calculateInterestAccumulated = calculateInterestAccumulated;
|
|
129
|
+
function calculateWithdrawLimit(bank, now) {
|
|
130
|
+
const bankDepositTokenAmount = getTokenAmount(bank.depositBalance, bank, types_1.BankBalanceType.DEPOSIT);
|
|
131
|
+
const bankBorrowTokenAmount = getTokenAmount(bank.borrowBalance, bank, types_1.BankBalanceType.BORROW);
|
|
132
|
+
const twentyFourHours = new anchor_1.BN(60 * 60 * 24);
|
|
133
|
+
const sinceLast = now.sub(bank.lastUpdated);
|
|
134
|
+
const sinceStart = anchor_1.BN.max(numericConstants_1.ZERO, twentyFourHours.sub(sinceLast));
|
|
135
|
+
const borrowTokenTwapLive = bank.borrowTokenTwap
|
|
136
|
+
.mul(sinceStart)
|
|
137
|
+
.add(bankBorrowTokenAmount.mul(sinceLast))
|
|
138
|
+
.div(sinceLast.add(sinceLast));
|
|
139
|
+
const depositTokenTwapLive = bank.depositTokenTwap
|
|
140
|
+
.mul(sinceStart)
|
|
141
|
+
.add(bankDepositTokenAmount.mul(sinceLast))
|
|
142
|
+
.div(sinceLast.add(sinceLast));
|
|
143
|
+
const maxBorrowTokens = anchor_1.BN.min(anchor_1.BN.max(bankDepositTokenAmount.div(new anchor_1.BN(6)), borrowTokenTwapLive.add(borrowTokenTwapLive.div(new anchor_1.BN(5)))), bankDepositTokenAmount.sub(bankDepositTokenAmount.div(new anchor_1.BN(10)))); // between ~15-90% utilization with friction on twap
|
|
144
|
+
const minDepositTokens = depositTokenTwapLive.sub(anchor_1.BN.min(anchor_1.BN.max(depositTokenTwapLive.div(new anchor_1.BN(5)), bank.withdrawGuardThreshold), depositTokenTwapLive));
|
|
145
|
+
return {
|
|
146
|
+
borrowLimit: maxBorrowTokens.sub(bankBorrowTokenAmount),
|
|
147
|
+
withdrawLimit: bankDepositTokenAmount.sub(minDepositTokens),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
exports.calculateWithdrawLimit = calculateWithdrawLimit;
|
package/lib/math/oracles.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/// <reference types="bn.js" />
|
|
1
2
|
import { AMM, OracleGuardRails } from '../types';
|
|
2
3
|
import { OraclePriceData } from '../oracles/types';
|
|
4
|
+
import { BN } from '../index';
|
|
3
5
|
export declare function isOracleValid(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, slot: number): boolean;
|
|
6
|
+
export declare function isOracleTooDivergent(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, now: BN): boolean;
|
package/lib/math/oracles.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isOracleValid = void 0;
|
|
3
|
+
exports.isOracleTooDivergent = exports.isOracleValid = void 0;
|
|
4
4
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
5
5
|
const index_1 = require("../index");
|
|
6
6
|
function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
|
|
7
|
-
const isOraclePriceNonPositive = oraclePriceData.price.
|
|
7
|
+
const isOraclePriceNonPositive = oraclePriceData.price.lte(numericConstants_1.ZERO);
|
|
8
8
|
const isOraclePriceTooVolatile = oraclePriceData.price
|
|
9
9
|
.div(index_1.BN.max(numericConstants_1.ONE, amm.lastOraclePriceTwap))
|
|
10
10
|
.gt(oracleGuardRails.validity.tooVolatileRatio) ||
|
|
11
11
|
amm.lastOraclePriceTwap
|
|
12
12
|
.div(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.price))
|
|
13
13
|
.gt(oracleGuardRails.validity.tooVolatileRatio);
|
|
14
|
-
const isConfidenceTooLarge =
|
|
15
|
-
.
|
|
16
|
-
.
|
|
14
|
+
const isConfidenceTooLarge = new index_1.BN(amm.baseSpread)
|
|
15
|
+
.add(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.confidence))
|
|
16
|
+
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
17
|
+
.div(oraclePriceData.price)
|
|
18
|
+
.gt(new index_1.BN(amm.maxSpread));
|
|
17
19
|
const oracleIsStale = oraclePriceData.slot
|
|
18
20
|
.sub(new index_1.BN(slot))
|
|
19
21
|
.gt(oracleGuardRails.validity.slotsBeforeStale);
|
|
@@ -24,3 +26,21 @@ function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
|
|
|
24
26
|
isConfidenceTooLarge);
|
|
25
27
|
}
|
|
26
28
|
exports.isOracleValid = isOracleValid;
|
|
29
|
+
function isOracleTooDivergent(amm, oraclePriceData, oracleGuardRails, now) {
|
|
30
|
+
const sinceLastUpdate = now.sub(amm.lastOraclePriceTwapTs);
|
|
31
|
+
const sinceStart = index_1.BN.max(numericConstants_1.ZERO, new index_1.BN(60 * 5).sub(sinceLastUpdate));
|
|
32
|
+
const oracleTwap5min = amm.lastOraclePriceTwap5min
|
|
33
|
+
.mul(sinceStart)
|
|
34
|
+
.add(oraclePriceData.price)
|
|
35
|
+
.mul(sinceLastUpdate)
|
|
36
|
+
.div(sinceStart.add(sinceLastUpdate));
|
|
37
|
+
const oracleSpread = oracleTwap5min.sub(oraclePriceData.price);
|
|
38
|
+
const oracleSpreadPct = oracleSpread
|
|
39
|
+
.mul(numericConstants_1.MARK_PRICE_PRECISION)
|
|
40
|
+
.div(oracleTwap5min);
|
|
41
|
+
const tooDivergent = oracleSpreadPct
|
|
42
|
+
.abs()
|
|
43
|
+
.gte(numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(oracleGuardRails.priceDivergence.markOracleDivergenceNumerator).div(oracleGuardRails.priceDivergence.markOracleDivergenceDenominator));
|
|
44
|
+
return tooDivergent;
|
|
45
|
+
}
|
|
46
|
+
exports.isOracleTooDivergent = isOracleTooDivergent;
|
package/lib/math/position.js
CHANGED
|
@@ -117,7 +117,8 @@ exports.calculatePositionFundingPNL = calculatePositionFundingPNL;
|
|
|
117
117
|
function positionIsAvailable(position) {
|
|
118
118
|
return (position.baseAssetAmount.eq(numericConstants_1.ZERO) &&
|
|
119
119
|
position.openOrders.eq(numericConstants_1.ZERO) &&
|
|
120
|
-
position.quoteAssetAmount.eq(numericConstants_1.ZERO)
|
|
120
|
+
position.quoteAssetAmount.eq(numericConstants_1.ZERO) &&
|
|
121
|
+
position.lpShares.eq(numericConstants_1.ZERO));
|
|
121
122
|
}
|
|
122
123
|
exports.positionIsAvailable = positionIsAvailable;
|
|
123
124
|
/**
|
package/lib/math/trade.js
CHANGED
|
@@ -113,8 +113,8 @@ function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType
|
|
|
113
113
|
const [newQuoteAssetReserve, newBaseAssetReserve] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, inputAssetType, amount, swapDirection);
|
|
114
114
|
const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
|
|
115
115
|
const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
|
|
116
|
-
const
|
|
117
|
-
return [acquiredBase, acquiredQuote,
|
|
116
|
+
const acquiredQuoteAssetAmount = (0, amm_1.calculateQuoteAssetAmountSwapped)(acquiredQuote.abs(), amm.pegMultiplier, swapDirection);
|
|
117
|
+
return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
|
|
118
118
|
}
|
|
119
119
|
exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
|
|
120
120
|
/**
|
package/lib/types.d.ts
CHANGED
|
@@ -203,6 +203,8 @@ export declare type LiquidationRecord = {
|
|
|
203
203
|
liquidateBorrow: LiquidateBorrowRecord;
|
|
204
204
|
liquidateBorrowForPerpPnl: LiquidateBorrowForPerpPnlRecord;
|
|
205
205
|
liquidatePerpPnlForDeposit: LiquidatePerpPnlForDepositRecord;
|
|
206
|
+
perpBankruptcy: PerpBankruptcyRecord;
|
|
207
|
+
borrowBankruptcy: BorrowBankruptcyRecord;
|
|
206
208
|
};
|
|
207
209
|
export declare class LiquidationType {
|
|
208
210
|
static readonly LIQUIDATE_PERP: {
|
|
@@ -217,6 +219,12 @@ export declare class LiquidationType {
|
|
|
217
219
|
static readonly LIQUIDATE_PERP_PNL_FOR_DEPOSIT: {
|
|
218
220
|
liquidatePerpPnlForDeposit: {};
|
|
219
221
|
};
|
|
222
|
+
static readonly PERP_BANKRUPTCY: {
|
|
223
|
+
perpBankruptcy: {};
|
|
224
|
+
};
|
|
225
|
+
static readonly BORROW_BANKRUPTCY: {
|
|
226
|
+
borrowBankruptcy: {};
|
|
227
|
+
};
|
|
220
228
|
}
|
|
221
229
|
export declare type LiquidatePerpRecord = {
|
|
222
230
|
marketIndex: BN;
|
|
@@ -255,14 +263,25 @@ export declare type LiquidatePerpPnlForDepositRecord = {
|
|
|
255
263
|
assetPrice: BN;
|
|
256
264
|
assetTransfer: BN;
|
|
257
265
|
};
|
|
266
|
+
export declare type PerpBankruptcyRecord = {
|
|
267
|
+
marketIndex: BN;
|
|
268
|
+
pnl: BN;
|
|
269
|
+
cumulativeFundingRateDelta: BN;
|
|
270
|
+
};
|
|
271
|
+
export declare type BorrowBankruptcyRecord = {
|
|
272
|
+
bankIndex: BN;
|
|
273
|
+
borrowAmount: BN;
|
|
274
|
+
cumulativeDepositInterestDelta: BN;
|
|
275
|
+
};
|
|
258
276
|
export declare type SettlePnlRecord = {
|
|
259
277
|
ts: BN;
|
|
278
|
+
user: PublicKey;
|
|
260
279
|
marketIndex: BN;
|
|
261
280
|
pnl: BN;
|
|
262
281
|
baseAssetAmount: BN;
|
|
263
282
|
quoteAssetAmountAfter: BN;
|
|
264
283
|
quoteEntryamount: BN;
|
|
265
|
-
|
|
284
|
+
settlePrice: BN;
|
|
266
285
|
};
|
|
267
286
|
export declare type OrderRecord = {
|
|
268
287
|
ts: BN;
|
|
@@ -357,6 +376,10 @@ export declare type BankAccount = {
|
|
|
357
376
|
maintenanceLiabilityWeight: BN;
|
|
358
377
|
liquidationFee: BN;
|
|
359
378
|
imfFactor: BN;
|
|
379
|
+
withdrawGuardThreshold: BN;
|
|
380
|
+
depositTokenTwap: BN;
|
|
381
|
+
borrowTokenTwap: BN;
|
|
382
|
+
utilizationTwap: BN;
|
|
360
383
|
};
|
|
361
384
|
export declare type PoolBalance = {
|
|
362
385
|
balance: BN;
|
|
@@ -368,8 +391,10 @@ export declare type AMM = {
|
|
|
368
391
|
lastFundingRate: BN;
|
|
369
392
|
lastFundingRateTs: BN;
|
|
370
393
|
lastMarkPriceTwap: BN;
|
|
394
|
+
lastMarkPriceTwap5min: BN;
|
|
371
395
|
lastMarkPriceTwapTs: BN;
|
|
372
396
|
lastOraclePriceTwap: BN;
|
|
397
|
+
lastOraclePriceTwap5min: BN;
|
|
373
398
|
lastOraclePriceTwapTs: BN;
|
|
374
399
|
lastOracleMarkSpreadPct: BN;
|
|
375
400
|
lastOracleConfPct: BN;
|
|
@@ -380,11 +405,16 @@ export declare type AMM = {
|
|
|
380
405
|
pegMultiplier: BN;
|
|
381
406
|
cumulativeFundingRateLong: BN;
|
|
382
407
|
cumulativeFundingRateShort: BN;
|
|
408
|
+
cumulativeFundingRateLp: BN;
|
|
383
409
|
cumulativeRepegRebateLong: BN;
|
|
384
410
|
cumulativeRepegRebateShort: BN;
|
|
385
411
|
totalFeeMinusDistributions: BN;
|
|
386
412
|
totalFeeWithdrawn: BN;
|
|
387
413
|
totalFee: BN;
|
|
414
|
+
cumulativeFundingPaymentPerLp: BN;
|
|
415
|
+
cumulativeFeePerLp: BN;
|
|
416
|
+
cumulativeNetBaseAssetAmountPerLp: BN;
|
|
417
|
+
userLpShares: BN;
|
|
388
418
|
minimumQuoteAssetTradeSize: BN;
|
|
389
419
|
baseAssetAmountStepSize: BN;
|
|
390
420
|
maxBaseAssetAmountRatio: number;
|
|
@@ -406,6 +436,8 @@ export declare type AMM = {
|
|
|
406
436
|
longSpread: BN;
|
|
407
437
|
shortSpread: BN;
|
|
408
438
|
maxSpread: number;
|
|
439
|
+
marketPosition: UserPosition;
|
|
440
|
+
marketPositionPerLp: UserPosition;
|
|
409
441
|
};
|
|
410
442
|
export declare type UserPosition = {
|
|
411
443
|
baseAssetAmount: BN;
|
|
@@ -416,14 +448,20 @@ export declare type UserPosition = {
|
|
|
416
448
|
openOrders: BN;
|
|
417
449
|
openBids: BN;
|
|
418
450
|
openAsks: BN;
|
|
451
|
+
realizedPnl: BN;
|
|
452
|
+
lpShares: BN;
|
|
453
|
+
lastFeePerLp: BN;
|
|
454
|
+
lastNetBaseAssetAmountPerLp: BN;
|
|
455
|
+
lastNetQuoteAssetAmountPerLp: BN;
|
|
419
456
|
};
|
|
420
|
-
export declare type
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
457
|
+
export declare type UserStatsAccount = {
|
|
458
|
+
numberOfUsers: number;
|
|
459
|
+
makerVolume30D: BN;
|
|
460
|
+
takerVolume30D: BN;
|
|
461
|
+
fillerVolume30D: BN;
|
|
462
|
+
lastMakerVolume30DTs: BN;
|
|
463
|
+
lastTakerVolume30DTs: BN;
|
|
464
|
+
lastFillerVolume30DTs: BN;
|
|
427
465
|
fees: {
|
|
428
466
|
totalFeePaid: BN;
|
|
429
467
|
totalFeeRebate: BN;
|
|
@@ -431,9 +469,16 @@ export declare type UserAccount = {
|
|
|
431
469
|
totalReferralReward: BN;
|
|
432
470
|
totalRefereeDiscount: BN;
|
|
433
471
|
};
|
|
472
|
+
};
|
|
473
|
+
export declare type UserAccount = {
|
|
474
|
+
authority: PublicKey;
|
|
475
|
+
name: number[];
|
|
476
|
+
userId: number;
|
|
477
|
+
bankBalances: UserBankBalance[];
|
|
434
478
|
positions: UserPosition[];
|
|
435
479
|
orders: Order[];
|
|
436
480
|
beingLiquidated: boolean;
|
|
481
|
+
bankrupt: boolean;
|
|
437
482
|
nextLiquidationId: number;
|
|
438
483
|
};
|
|
439
484
|
export declare type UserBankBalance = {
|
|
@@ -529,10 +574,12 @@ export declare const DefaultOrderParams: {
|
|
|
529
574
|
};
|
|
530
575
|
export declare type MakerInfo = {
|
|
531
576
|
maker: PublicKey;
|
|
577
|
+
makerStats: PublicKey;
|
|
532
578
|
order: Order;
|
|
533
579
|
};
|
|
534
580
|
export declare type TakerInfo = {
|
|
535
581
|
taker: PublicKey;
|
|
582
|
+
takerStats: PublicKey;
|
|
536
583
|
order: Order;
|
|
537
584
|
};
|
|
538
585
|
export interface IWallet {
|
package/lib/types.js
CHANGED
|
@@ -108,6 +108,12 @@ LiquidationType.LIQUIDATE_BORROW_FOR_PERP_PNL = {
|
|
|
108
108
|
LiquidationType.LIQUIDATE_PERP_PNL_FOR_DEPOSIT = {
|
|
109
109
|
liquidatePerpPnlForDeposit: {},
|
|
110
110
|
};
|
|
111
|
+
LiquidationType.PERP_BANKRUPTCY = {
|
|
112
|
+
perpBankruptcy: {},
|
|
113
|
+
};
|
|
114
|
+
LiquidationType.BORROW_BANKRUPTCY = {
|
|
115
|
+
borrowBankruptcy: {},
|
|
116
|
+
};
|
|
111
117
|
exports.DefaultOrderParams = {
|
|
112
118
|
orderType: OrderType.MARKET,
|
|
113
119
|
userOrderId: 0,
|
package/package.json
CHANGED
package/src/accounts/fetch.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
-
import { UserAccount } from '../types';
|
|
3
|
-
import {
|
|
2
|
+
import { UserAccount, UserStatsAccount } from '../types';
|
|
3
|
+
import {
|
|
4
|
+
getUserAccountPublicKey,
|
|
5
|
+
getUserStatsAccountPublicKey,
|
|
6
|
+
} from '../addresses/pda';
|
|
4
7
|
import { Program } from '@project-serum/anchor';
|
|
5
8
|
|
|
6
9
|
export async function fetchUserAccounts(
|
|
@@ -31,3 +34,25 @@ export async function fetchUserAccounts(
|
|
|
31
34
|
) as UserAccount;
|
|
32
35
|
});
|
|
33
36
|
}
|
|
37
|
+
|
|
38
|
+
export async function fetchUserStatsAccount(
|
|
39
|
+
connection: Connection,
|
|
40
|
+
program: Program,
|
|
41
|
+
authority: PublicKey
|
|
42
|
+
): Promise<UserStatsAccount | undefined> {
|
|
43
|
+
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
44
|
+
program.programId,
|
|
45
|
+
authority
|
|
46
|
+
);
|
|
47
|
+
const accountInfo = await connection.getAccountInfo(
|
|
48
|
+
userStatsPublicKey,
|
|
49
|
+
'confirmed'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return accountInfo
|
|
53
|
+
? (program.account.user.coder.accounts.decode(
|
|
54
|
+
'UserStats',
|
|
55
|
+
accountInfo.data
|
|
56
|
+
) as UserStatsAccount)
|
|
57
|
+
: undefined;
|
|
58
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataAndSlot,
|
|
3
|
+
AccountToPoll,
|
|
4
|
+
NotSubscribedError,
|
|
5
|
+
UserStatsAccountSubscriber,
|
|
6
|
+
UserStatsAccountEvents,
|
|
7
|
+
} from './types';
|
|
8
|
+
import { Program } from '@project-serum/anchor';
|
|
9
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
import { PublicKey } from '@solana/web3.js';
|
|
12
|
+
import { UserStatsAccount } from '../types';
|
|
13
|
+
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
14
|
+
import { capitalize } from './utils';
|
|
15
|
+
|
|
16
|
+
export class PollingUserStatsAccountSubscriber
|
|
17
|
+
implements UserStatsAccountSubscriber
|
|
18
|
+
{
|
|
19
|
+
isSubscribed: boolean;
|
|
20
|
+
program: Program;
|
|
21
|
+
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
22
|
+
userStatsAccountPublicKey: PublicKey;
|
|
23
|
+
|
|
24
|
+
accountLoader: BulkAccountLoader;
|
|
25
|
+
accountsToPoll = new Map<string, AccountToPoll>();
|
|
26
|
+
errorCallbackId?: string;
|
|
27
|
+
|
|
28
|
+
userStats?: DataAndSlot<UserStatsAccount>;
|
|
29
|
+
|
|
30
|
+
public constructor(
|
|
31
|
+
program: Program,
|
|
32
|
+
userStatsAccountPublicKey: PublicKey,
|
|
33
|
+
accountLoader: BulkAccountLoader
|
|
34
|
+
) {
|
|
35
|
+
this.isSubscribed = false;
|
|
36
|
+
this.program = program;
|
|
37
|
+
this.accountLoader = accountLoader;
|
|
38
|
+
this.eventEmitter = new EventEmitter();
|
|
39
|
+
this.userStatsAccountPublicKey = userStatsAccountPublicKey;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async subscribe(): Promise<boolean> {
|
|
43
|
+
if (this.isSubscribed) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await this.addToAccountLoader();
|
|
48
|
+
|
|
49
|
+
let subscriptionSucceeded = false;
|
|
50
|
+
let retries = 0;
|
|
51
|
+
while (!subscriptionSucceeded && retries < 5) {
|
|
52
|
+
await this.fetchIfUnloaded();
|
|
53
|
+
subscriptionSucceeded = this.didSubscriptionSucceed();
|
|
54
|
+
retries++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (subscriptionSucceeded) {
|
|
58
|
+
this.eventEmitter.emit('update');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.isSubscribed = subscriptionSucceeded;
|
|
62
|
+
return subscriptionSucceeded;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async addToAccountLoader(): Promise<void> {
|
|
66
|
+
if (this.accountsToPoll.size > 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.accountsToPoll.set(this.userStatsAccountPublicKey.toString(), {
|
|
71
|
+
key: 'userStats',
|
|
72
|
+
publicKey: this.userStatsAccountPublicKey,
|
|
73
|
+
eventType: 'userStatsAccountUpdate',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
77
|
+
accountToPoll.callbackId = this.accountLoader.addAccount(
|
|
78
|
+
accountToPoll.publicKey,
|
|
79
|
+
(buffer, slot) => {
|
|
80
|
+
if (!buffer) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const account = this.program.account[
|
|
85
|
+
accountToPoll.key
|
|
86
|
+
].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
|
|
87
|
+
this[accountToPoll.key] = { data: account, slot };
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
this.eventEmitter.emit(accountToPoll.eventType, account);
|
|
90
|
+
this.eventEmitter.emit('update');
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
96
|
+
this.eventEmitter.emit('error', error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async fetchIfUnloaded(): Promise<void> {
|
|
101
|
+
let shouldFetch = false;
|
|
102
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
103
|
+
if (this[accountToPoll.key] === undefined) {
|
|
104
|
+
shouldFetch = true;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (shouldFetch) {
|
|
110
|
+
await this.fetch();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async fetch(): Promise<void> {
|
|
115
|
+
await this.accountLoader.load();
|
|
116
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
117
|
+
const { buffer, slot } = this.accountLoader.getBufferAndSlot(
|
|
118
|
+
accountToPoll.publicKey
|
|
119
|
+
);
|
|
120
|
+
if (buffer) {
|
|
121
|
+
const account = this.program.account[
|
|
122
|
+
accountToPoll.key
|
|
123
|
+
].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
|
|
124
|
+
this[accountToPoll.key] = { data: account, slot };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
didSubscriptionSucceed(): boolean {
|
|
130
|
+
let success = true;
|
|
131
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
132
|
+
if (!this[accountToPoll.key]) {
|
|
133
|
+
success = false;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return success;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async unsubscribe(): Promise<void> {
|
|
141
|
+
if (!this.isSubscribed) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
146
|
+
this.accountLoader.removeAccount(
|
|
147
|
+
accountToPoll.publicKey,
|
|
148
|
+
accountToPoll.callbackId
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
153
|
+
this.errorCallbackId = undefined;
|
|
154
|
+
|
|
155
|
+
this.accountsToPoll.clear();
|
|
156
|
+
|
|
157
|
+
this.isSubscribed = false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
assertIsSubscribed(): void {
|
|
161
|
+
if (!this.isSubscribed) {
|
|
162
|
+
throw new NotSubscribedError(
|
|
163
|
+
'You must call `subscribe` before using this function'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
|
|
169
|
+
this.assertIsSubscribed();
|
|
170
|
+
return this.userStats;
|
|
171
|
+
}
|
|
172
|
+
}
|
package/src/accounts/types.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
OracleSource,
|
|
5
5
|
StateAccount,
|
|
6
6
|
UserAccount,
|
|
7
|
+
UserStatsAccount,
|
|
7
8
|
} from '../types';
|
|
8
9
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
9
10
|
import { EventEmitter } from 'events';
|
|
@@ -130,3 +131,20 @@ export type DataAndSlot<T> = {
|
|
|
130
131
|
data: T;
|
|
131
132
|
slot: number;
|
|
132
133
|
};
|
|
134
|
+
|
|
135
|
+
export interface UserStatsAccountEvents {
|
|
136
|
+
userStatsAccountUpdate: (payload: UserStatsAccount) => void;
|
|
137
|
+
update: void;
|
|
138
|
+
error: (e: Error) => void;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface UserStatsAccountSubscriber {
|
|
142
|
+
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
143
|
+
isSubscribed: boolean;
|
|
144
|
+
|
|
145
|
+
subscribe(): Promise<boolean>;
|
|
146
|
+
fetch(): Promise<void>;
|
|
147
|
+
unsubscribe(): Promise<void>;
|
|
148
|
+
|
|
149
|
+
getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount>;
|
|
150
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataAndSlot,
|
|
3
|
+
AccountSubscriber,
|
|
4
|
+
NotSubscribedError,
|
|
5
|
+
UserStatsAccountSubscriber,
|
|
6
|
+
UserStatsAccountEvents,
|
|
7
|
+
} from './types';
|
|
8
|
+
import { Program } from '@project-serum/anchor';
|
|
9
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
import { PublicKey } from '@solana/web3.js';
|
|
12
|
+
import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber';
|
|
13
|
+
import { UserStatsAccount } from '../types';
|
|
14
|
+
|
|
15
|
+
export class WebSocketUserStatsAccountSubscriber
|
|
16
|
+
implements UserStatsAccountSubscriber
|
|
17
|
+
{
|
|
18
|
+
isSubscribed: boolean;
|
|
19
|
+
program: Program;
|
|
20
|
+
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
21
|
+
userStatsAccountPublicKey: PublicKey;
|
|
22
|
+
|
|
23
|
+
userStatsAccountSubscriber: AccountSubscriber<UserStatsAccount>;
|
|
24
|
+
|
|
25
|
+
public constructor(program: Program, userStatsAccountPublicKey: PublicKey) {
|
|
26
|
+
this.isSubscribed = false;
|
|
27
|
+
this.program = program;
|
|
28
|
+
this.userStatsAccountPublicKey = userStatsAccountPublicKey;
|
|
29
|
+
this.eventEmitter = new EventEmitter();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async subscribe(): Promise<boolean> {
|
|
33
|
+
if (this.isSubscribed) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.userStatsAccountSubscriber = new WebSocketAccountSubscriber(
|
|
38
|
+
'userStats',
|
|
39
|
+
this.program,
|
|
40
|
+
this.userStatsAccountPublicKey
|
|
41
|
+
);
|
|
42
|
+
await this.userStatsAccountSubscriber.subscribe(
|
|
43
|
+
(data: UserStatsAccount) => {
|
|
44
|
+
this.eventEmitter.emit('userStatsAccountUpdate', data);
|
|
45
|
+
this.eventEmitter.emit('update');
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
this.eventEmitter.emit('update');
|
|
50
|
+
this.isSubscribed = true;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async fetch(): Promise<void> {
|
|
55
|
+
await Promise.all([this.userStatsAccountSubscriber.fetch()]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async unsubscribe(): Promise<void> {
|
|
59
|
+
if (!this.isSubscribed) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await Promise.all([this.userStatsAccountSubscriber.unsubscribe()]);
|
|
64
|
+
|
|
65
|
+
this.isSubscribed = false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
assertIsSubscribed(): void {
|
|
69
|
+
if (!this.isSubscribed) {
|
|
70
|
+
throw new NotSubscribedError(
|
|
71
|
+
'You must call `subscribe` before using this function'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
|
|
77
|
+
this.assertIsSubscribed();
|
|
78
|
+
return this.userStatsAccountSubscriber.dataAndSlot;
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/addresses/pda.ts
CHANGED
|
@@ -57,6 +57,19 @@ export function getUserAccountPublicKeySync(
|
|
|
57
57
|
)[0];
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export function getUserStatsAccountPublicKey(
|
|
61
|
+
programId: PublicKey,
|
|
62
|
+
authority: PublicKey
|
|
63
|
+
): PublicKey {
|
|
64
|
+
return anchor.web3.PublicKey.findProgramAddressSync(
|
|
65
|
+
[
|
|
66
|
+
Buffer.from(anchor.utils.bytes.utf8.encode('user_stats')),
|
|
67
|
+
authority.toBuffer(),
|
|
68
|
+
],
|
|
69
|
+
programId
|
|
70
|
+
)[0];
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
export async function getMarketPublicKey(
|
|
61
74
|
programId: PublicKey,
|
|
62
75
|
marketIndex: BN
|
package/src/admin.ts
CHANGED
|
@@ -575,6 +575,35 @@ export class Admin extends ClearingHouse {
|
|
|
575
575
|
});
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
+
public async updateBankWithdrawGuardThreshold(
|
|
579
|
+
bankIndex: BN,
|
|
580
|
+
withdrawGuardThreshold: BN
|
|
581
|
+
): Promise<TransactionSignature> {
|
|
582
|
+
return await this.program.rpc.updateBankWithdrawGuardThreshold(
|
|
583
|
+
withdrawGuardThreshold,
|
|
584
|
+
{
|
|
585
|
+
accounts: {
|
|
586
|
+
admin: this.wallet.publicKey,
|
|
587
|
+
state: await this.getStatePublicKey(),
|
|
588
|
+
bank: await getBankPublicKey(this.program.programId, bankIndex),
|
|
589
|
+
},
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
public async updateLpCooldownTime(
|
|
595
|
+
marketIndex: BN,
|
|
596
|
+
cooldownTime: BN
|
|
597
|
+
): Promise<TransactionSignature> {
|
|
598
|
+
return await this.program.rpc.updateLpCooldownTime(cooldownTime, {
|
|
599
|
+
accounts: {
|
|
600
|
+
admin: this.wallet.publicKey,
|
|
601
|
+
state: await this.getStatePublicKey(),
|
|
602
|
+
market: await getMarketPublicKey(this.program.programId, marketIndex),
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
578
607
|
public async updateMarketOracle(
|
|
579
608
|
marketIndex: BN,
|
|
580
609
|
oracle: PublicKey,
|
|
@@ -662,7 +691,6 @@ export class Admin extends ClearingHouse {
|
|
|
662
691
|
},
|
|
663
692
|
});
|
|
664
693
|
}
|
|
665
|
-
|
|
666
694
|
public async updateExchangePaused(
|
|
667
695
|
exchangePaused: boolean
|
|
668
696
|
): Promise<TransactionSignature> {
|