@drift-labs/sdk 2.83.0-beta.9 → 2.84.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/VERSION +1 -1
- package/lib/adminClient.d.ts +2 -2
- package/lib/adminClient.js +7 -9
- package/lib/constants/perpMarkets.js +10 -0
- package/lib/dlob/DLOB.d.ts +5 -5
- package/lib/dlob/DLOB.js +8 -8
- package/lib/driftClient.d.ts +1 -1
- package/lib/driftClient.js +2 -2
- package/lib/idl/drift.json +1 -1
- package/lib/math/auction.js +2 -2
- package/lib/math/orders.js +1 -1
- package/lib/userMap/userMap.d.ts +3 -0
- package/lib/userMap/userMap.js +103 -1
- package/lib/userMap/userMapConfig.d.ts +8 -0
- package/package.json +1 -1
- package/src/adminClient.ts +12 -10
- package/src/constants/perpMarkets.ts +10 -0
- package/src/dlob/DLOB.ts +17 -9
- package/src/driftClient.ts +8 -4
- package/src/idl/drift.json +1 -1
- package/src/math/auction.ts +2 -2
- package/src/math/orders.ts +5 -2
- package/src/userMap/userMap.ts +131 -0
- package/src/userMap/userMapConfig.ts +12 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.84.0-beta.0
|
package/lib/adminClient.d.ts
CHANGED
|
@@ -7,10 +7,10 @@ export declare class AdminClient extends DriftClient {
|
|
|
7
7
|
initialize(usdcMint: PublicKey, _adminControlsPrices: boolean): Promise<[TransactionSignature]>;
|
|
8
8
|
initializeSpotMarket(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidatorFee?: number, ifLiquidationFee?: number, activeStatus?: boolean, assetTier?: {
|
|
9
9
|
collateral: {};
|
|
10
|
-
}, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string): Promise<TransactionSignature>;
|
|
10
|
+
}, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string, marketIndex?: number): Promise<TransactionSignature>;
|
|
11
11
|
getInitializeSpotMarketIx(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidatorFee?: number, ifLiquidationFee?: number, activeStatus?: boolean, assetTier?: {
|
|
12
12
|
collateral: {};
|
|
13
|
-
}, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string): Promise<TransactionInstruction>;
|
|
13
|
+
}, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string, marketIndex?: number): Promise<TransactionInstruction>;
|
|
14
14
|
deleteInitializedSpotMarket(marketIndex: number): Promise<TransactionSignature>;
|
|
15
15
|
getDeleteInitializedSpotMarketIx(marketIndex: number): Promise<TransactionInstruction>;
|
|
16
16
|
initializeSerumFulfillmentConfig(marketIndex: number, serumMarket: PublicKey, serumProgram: PublicKey): Promise<TransactionSignature>;
|
package/lib/adminClient.js
CHANGED
|
@@ -61,12 +61,11 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
61
61
|
const { txSig } = await super.sendTransaction(tx, [], this.opts);
|
|
62
62
|
return [txSig];
|
|
63
63
|
}
|
|
64
|
-
async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME) {
|
|
65
|
-
const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
|
|
66
|
-
const initializeIx = await this.getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor, liquidatorFee, ifLiquidationFee, activeStatus, assetTier, scaleInitialAssetWeightStart, withdrawGuardThreshold, orderTickSize, orderStepSize, ifTotalFactor, name);
|
|
64
|
+
async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME, marketIndex) {
|
|
65
|
+
const spotMarketIndex = marketIndex !== null && marketIndex !== void 0 ? marketIndex : this.getStateAccount().numberOfSpotMarkets;
|
|
66
|
+
const initializeIx = await this.getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor, liquidatorFee, ifLiquidationFee, activeStatus, assetTier, scaleInitialAssetWeightStart, withdrawGuardThreshold, orderTickSize, orderStepSize, ifTotalFactor, name, marketIndex);
|
|
67
67
|
const tx = await this.buildTransaction(initializeIx);
|
|
68
68
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
69
|
-
// const { txSig } = await this.sendTransaction(initializeTx, [], this.opts);
|
|
70
69
|
await this.accountSubscriber.addSpotMarket(spotMarketIndex);
|
|
71
70
|
await this.accountSubscriber.addOracle({
|
|
72
71
|
source: oracleSource,
|
|
@@ -75,8 +74,8 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
75
74
|
await this.accountSubscriber.setSpotOracleMap();
|
|
76
75
|
return txSig;
|
|
77
76
|
}
|
|
78
|
-
async getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME) {
|
|
79
|
-
const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
|
|
77
|
+
async getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME, marketIndex) {
|
|
78
|
+
const spotMarketIndex = marketIndex !== null && marketIndex !== void 0 ? marketIndex : this.getStateAccount().numberOfSpotMarkets;
|
|
80
79
|
const spotMarket = await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, spotMarketIndex);
|
|
81
80
|
const spotMarketVault = await (0, pda_1.getSpotMarketVaultPublicKey)(this.program.programId, spotMarketIndex);
|
|
82
81
|
const insuranceFundVault = await (0, pda_1.getInsuranceFundVaultPublicKey)(this.program.programId, spotMarketIndex);
|
|
@@ -184,7 +183,7 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
184
183
|
while (this.getStateAccount().numberOfMarkets <= currentPerpMarketIndex) {
|
|
185
184
|
await this.fetchAccounts();
|
|
186
185
|
}
|
|
187
|
-
await this.accountSubscriber.addPerpMarket(
|
|
186
|
+
await this.accountSubscriber.addPerpMarket(marketIndex);
|
|
188
187
|
await this.accountSubscriber.addOracle({
|
|
189
188
|
source: oracleSource,
|
|
190
189
|
publicKey: priceOracle,
|
|
@@ -193,8 +192,7 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
193
192
|
return txSig;
|
|
194
193
|
}
|
|
195
194
|
async getInitializePerpMarketIx(marketIndex, priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION, oracleSource = types_1.OracleSource.PYTH, contractTier = types_1.ContractTier.SPECULATIVE, marginRatioInitial = 2000, marginRatioMaintenance = 500, liquidatorFee = 0, ifLiquidatorFee = 10000, imfFactor = 0, activeStatus = true, baseSpread = 0, maxSpread = 142500, maxOpenInterest = numericConstants_1.ZERO, maxRevenueWithdrawPerPeriod = numericConstants_1.ZERO, quoteMaxInsurance = numericConstants_1.ZERO, orderStepSize = numericConstants_1.BASE_PRECISION.divn(10000), orderTickSize = numericConstants_1.PRICE_PRECISION.divn(100000), minOrderSize = numericConstants_1.BASE_PRECISION.divn(10000), concentrationCoefScale = numericConstants_1.ONE, curveUpdateIntensity = 0, ammJitIntensity = 0, name = userName_1.DEFAULT_MARKET_NAME) {
|
|
196
|
-
const
|
|
197
|
-
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, currentPerpMarketIndex);
|
|
195
|
+
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, marketIndex);
|
|
198
196
|
const nameBuffer = (0, userName_1.encodeName)(name);
|
|
199
197
|
return await this.program.instruction.initializePerpMarket(marketIndex, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier, oracleSource, contractTier, marginRatioInitial, marginRatioMaintenance, liquidatorFee, ifLiquidatorFee, imfFactor, activeStatus, baseSpread, maxSpread, maxOpenInterest, maxRevenueWithdrawPerPeriod, quoteMaxInsurance, orderStepSize, orderTickSize, minOrderSize, concentrationCoefScale, curveUpdateIntensity, ammJitIntensity, nameBuffer, {
|
|
200
198
|
accounts: {
|
|
@@ -566,6 +566,16 @@ exports.MainnetPerpMarkets = [
|
|
|
566
566
|
launchTs: 1716595200000,
|
|
567
567
|
oracleSource: __1.OracleSource.SWITCHBOARD,
|
|
568
568
|
},
|
|
569
|
+
{
|
|
570
|
+
fullName: 'Sanctum',
|
|
571
|
+
category: ['LST', 'Solana'],
|
|
572
|
+
symbol: 'CLOUD-PERP',
|
|
573
|
+
baseAssetSymbol: 'CLOUD',
|
|
574
|
+
marketIndex: 31,
|
|
575
|
+
oracle: new web3_js_1.PublicKey('C7UxgCodaEy4yqwTe3a4QXfsG7LnpMGGQdEqaxDae4b8'),
|
|
576
|
+
launchTs: 1717597648000,
|
|
577
|
+
oracleSource: __1.OracleSource.Prelaunch,
|
|
578
|
+
},
|
|
569
579
|
];
|
|
570
580
|
exports.PerpMarkets = {
|
|
571
581
|
devnet: exports.DevnetPerpMarkets,
|
package/lib/dlob/DLOB.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ type OrderBookCallback = () => void;
|
|
|
31
31
|
* Receives a DLOBNode and is expected to return true if the node should
|
|
32
32
|
* be taken into account when generating, or false otherwise.
|
|
33
33
|
*
|
|
34
|
-
* Currently used in
|
|
34
|
+
* Currently used in functions that rely on getBestNode
|
|
35
35
|
*/
|
|
36
36
|
export type DLOBFilterFcn = (node: DLOBNode) => boolean;
|
|
37
37
|
export type NodeToFill = {
|
|
@@ -79,13 +79,13 @@ export declare class DLOB {
|
|
|
79
79
|
findNodesCrossingFallbackLiquidity(marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, nodeGenerator: Generator<DLOBNode>, doesCross: (nodePrice: BN | undefined) => boolean, minAuctionDuration: number): NodeToFill[];
|
|
80
80
|
findExpiredNodesToFill(marketIndex: number, ts: number, marketType: MarketType): NodeToFill[];
|
|
81
81
|
findJitAuctionNodesToFill(marketIndex: number, slot: number, oraclePriceData: OraclePriceData, marketType: MarketType): NodeToFill[];
|
|
82
|
-
getTakingBids(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
83
|
-
getTakingAsks(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
82
|
+
getTakingBids(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
83
|
+
getTakingAsks(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
84
84
|
private getBestNode;
|
|
85
85
|
getRestingLimitAsks(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
86
86
|
getRestingLimitBids(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
87
|
-
getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
88
|
-
getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
87
|
+
getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
88
|
+
getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
|
|
89
89
|
findCrossingRestingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
90
90
|
determineMakerAndTaker(askNode: DLOBNode, bidNode: DLOBNode): {
|
|
91
91
|
takerNode: DLOBNode;
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -565,7 +565,7 @@ class DLOB {
|
|
|
565
565
|
}
|
|
566
566
|
return nodesToFill;
|
|
567
567
|
}
|
|
568
|
-
*getTakingBids(marketIndex, marketType, slot, oraclePriceData) {
|
|
568
|
+
*getTakingBids(marketIndex, marketType, slot, oraclePriceData, filterFcn) {
|
|
569
569
|
const marketTypeStr = (0, __1.getVariant)(marketType);
|
|
570
570
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
571
571
|
if (!orderLists) {
|
|
@@ -578,9 +578,9 @@ class DLOB {
|
|
|
578
578
|
];
|
|
579
579
|
yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
|
|
580
580
|
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
581
|
-
});
|
|
581
|
+
}, filterFcn);
|
|
582
582
|
}
|
|
583
|
-
*getTakingAsks(marketIndex, marketType, slot, oraclePriceData) {
|
|
583
|
+
*getTakingAsks(marketIndex, marketType, slot, oraclePriceData, filterFcn) {
|
|
584
584
|
const marketTypeStr = (0, __1.getVariant)(marketType);
|
|
585
585
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
586
586
|
if (!orderLists) {
|
|
@@ -593,7 +593,7 @@ class DLOB {
|
|
|
593
593
|
];
|
|
594
594
|
yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
|
|
595
595
|
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
596
|
-
});
|
|
596
|
+
}, filterFcn);
|
|
597
597
|
}
|
|
598
598
|
*getBestNode(generatorList, oraclePriceData, slot, compareFcn, filterFcn) {
|
|
599
599
|
const generators = generatorList.map((generator) => {
|
|
@@ -675,7 +675,7 @@ class DLOB {
|
|
|
675
675
|
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
676
676
|
}, filterFcn);
|
|
677
677
|
}
|
|
678
|
-
*getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
678
|
+
*getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData, filterFcn) {
|
|
679
679
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
680
680
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
681
681
|
}
|
|
@@ -706,9 +706,9 @@ class DLOB {
|
|
|
706
706
|
return bestNode
|
|
707
707
|
.getPrice(oraclePriceData, slot)
|
|
708
708
|
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
709
|
-
});
|
|
709
|
+
}, filterFcn);
|
|
710
710
|
}
|
|
711
|
-
*getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
|
|
711
|
+
*getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData, filterFcn) {
|
|
712
712
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
713
713
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
714
714
|
}
|
|
@@ -739,7 +739,7 @@ class DLOB {
|
|
|
739
739
|
return bestNode
|
|
740
740
|
.getPrice(oraclePriceData, slot)
|
|
741
741
|
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
742
|
-
});
|
|
742
|
+
}, filterFcn);
|
|
743
743
|
}
|
|
744
744
|
findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
|
|
745
745
|
const nodesToFill = new Array();
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -593,7 +593,7 @@ export declare class DriftClient {
|
|
|
593
593
|
settleeUserAccount: UserAccount;
|
|
594
594
|
}[], marketIndexes: number[], opts?: {
|
|
595
595
|
filterInvalidMarkets?: boolean;
|
|
596
|
-
}): Promise<TransactionSignature>;
|
|
596
|
+
}, txParams?: TxParams): Promise<TransactionSignature>;
|
|
597
597
|
getSettlePNLsIxs(users: {
|
|
598
598
|
settleeUserAccountPublicKey: PublicKey;
|
|
599
599
|
settleeUserAccount: UserAccount;
|
package/lib/driftClient.js
CHANGED
|
@@ -2949,7 +2949,7 @@ class DriftClient {
|
|
|
2949
2949
|
remainingAccounts,
|
|
2950
2950
|
});
|
|
2951
2951
|
}
|
|
2952
|
-
async settlePNLs(users, marketIndexes, opts) {
|
|
2952
|
+
async settlePNLs(users, marketIndexes, opts, txParams) {
|
|
2953
2953
|
const filterInvalidMarkets = opts === null || opts === void 0 ? void 0 : opts.filterInvalidMarkets;
|
|
2954
2954
|
// # Filter market indexes by markets with valid oracle
|
|
2955
2955
|
const marketIndexToSettle = filterInvalidMarkets
|
|
@@ -2969,7 +2969,7 @@ class DriftClient {
|
|
|
2969
2969
|
}
|
|
2970
2970
|
// # Settle filtered market indexes
|
|
2971
2971
|
const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
|
|
2972
|
-
const tx = await this.buildTransaction(ixs, {
|
|
2972
|
+
const tx = await this.buildTransaction(ixs, txParams !== null && txParams !== void 0 ? txParams : {
|
|
2973
2973
|
computeUnits: 1400000,
|
|
2974
2974
|
});
|
|
2975
2975
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
package/lib/idl/drift.json
CHANGED
package/lib/math/auction.js
CHANGED
|
@@ -69,7 +69,7 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
|
|
|
69
69
|
const deltaDenominator = new _1.BN(order.auctionDuration);
|
|
70
70
|
const deltaNumerator = _1.BN.min(slotsElapsed, deltaDenominator);
|
|
71
71
|
if (deltaDenominator.eq(_1.ZERO)) {
|
|
72
|
-
return oraclePrice.add(order.auctionEndPrice);
|
|
72
|
+
return _1.BN.max(oraclePrice.add(order.auctionEndPrice), _1.ONE);
|
|
73
73
|
}
|
|
74
74
|
let priceOffsetDelta;
|
|
75
75
|
if ((0, types_1.isVariant)(order.direction, 'long')) {
|
|
@@ -91,7 +91,7 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
|
|
|
91
91
|
else {
|
|
92
92
|
priceOffset = order.auctionStartPrice.sub(priceOffsetDelta);
|
|
93
93
|
}
|
|
94
|
-
return oraclePrice.add(priceOffset);
|
|
94
|
+
return _1.BN.max(oraclePrice.add(priceOffset), _1.ONE);
|
|
95
95
|
}
|
|
96
96
|
exports.getAuctionPriceForOracleOffsetAuction = getAuctionPriceForOracleOffsetAuction;
|
|
97
97
|
function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }) {
|
package/lib/math/orders.js
CHANGED
|
@@ -102,7 +102,7 @@ function getLimitPrice(order, oraclePriceData, slot, fallbackPrice) {
|
|
|
102
102
|
limitPrice = (0, auction_1.getAuctionPrice)(order, slot, oraclePriceData.price);
|
|
103
103
|
}
|
|
104
104
|
else if (order.oraclePriceOffset !== 0) {
|
|
105
|
-
limitPrice = oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset));
|
|
105
|
+
limitPrice = anchor_1.BN.max(oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset)), numericConstants_1.ONE);
|
|
106
106
|
}
|
|
107
107
|
else if (order.price.eq(numericConstants_1.ZERO)) {
|
|
108
108
|
limitPrice = fallbackPrice;
|
package/lib/userMap/userMap.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class UserMap implements UserMapInterface {
|
|
|
29
29
|
private stateAccountUpdateCallback;
|
|
30
30
|
private decode;
|
|
31
31
|
private mostRecentSlot;
|
|
32
|
+
private syncConfig;
|
|
32
33
|
private syncPromise?;
|
|
33
34
|
private syncPromiseResolver;
|
|
34
35
|
/**
|
|
@@ -78,6 +79,8 @@ export declare class UserMap implements UserMapInterface {
|
|
|
78
79
|
*/
|
|
79
80
|
getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
|
|
80
81
|
sync(): Promise<void>;
|
|
82
|
+
private defaultSync;
|
|
83
|
+
private paginatedSync;
|
|
81
84
|
unsubscribe(): Promise<void>;
|
|
82
85
|
updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
|
|
83
86
|
updateLatestSlot(slot: number): void;
|
package/lib/userMap/userMap.js
CHANGED
|
@@ -15,7 +15,7 @@ class UserMap {
|
|
|
15
15
|
* Constructs a new UserMap instance.
|
|
16
16
|
*/
|
|
17
17
|
constructor(config) {
|
|
18
|
-
var _a, _b, _c, _d;
|
|
18
|
+
var _a, _b, _c, _d, _e;
|
|
19
19
|
this.userMap = new Map();
|
|
20
20
|
this.stateAccountUpdateCallback = async (state) => {
|
|
21
21
|
if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
|
|
@@ -64,6 +64,9 @@ class UserMap {
|
|
|
64
64
|
decodeFn,
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
+
this.syncConfig = (_e = config.syncConfig) !== null && _e !== void 0 ? _e : {
|
|
68
|
+
type: 'default',
|
|
69
|
+
};
|
|
67
70
|
}
|
|
68
71
|
async subscribe() {
|
|
69
72
|
if (this.size() > 0) {
|
|
@@ -230,6 +233,14 @@ class UserMap {
|
|
|
230
233
|
return userAuthKeys;
|
|
231
234
|
}
|
|
232
235
|
async sync() {
|
|
236
|
+
if (this.syncConfig.type === 'default') {
|
|
237
|
+
return this.defaultSync();
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
return this.paginatedSync();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async defaultSync() {
|
|
233
244
|
var _a;
|
|
234
245
|
if (this.syncPromise) {
|
|
235
246
|
return this.syncPromise;
|
|
@@ -298,6 +309,97 @@ class UserMap {
|
|
|
298
309
|
this.syncPromise = undefined;
|
|
299
310
|
}
|
|
300
311
|
}
|
|
312
|
+
async paginatedSync() {
|
|
313
|
+
var _a, _b;
|
|
314
|
+
if (this.syncPromise) {
|
|
315
|
+
return this.syncPromise;
|
|
316
|
+
}
|
|
317
|
+
this.syncPromise = new Promise((resolve) => {
|
|
318
|
+
this.syncPromiseResolver = resolve;
|
|
319
|
+
});
|
|
320
|
+
try {
|
|
321
|
+
const accountsPrefetch = await this.connection.getProgramAccounts(this.driftClient.program.programId, {
|
|
322
|
+
dataSlice: { offset: 0, length: 0 },
|
|
323
|
+
filters: [
|
|
324
|
+
(0, memcmp_1.getUserFilter)(),
|
|
325
|
+
...(!this.includeIdle ? [(0, memcmp_1.getNonIdleUserFilter)()] : []),
|
|
326
|
+
],
|
|
327
|
+
});
|
|
328
|
+
const accountPublicKeys = accountsPrefetch.map((account) => account.pubkey);
|
|
329
|
+
const limitConcurrency = async (tasks, limit) => {
|
|
330
|
+
const executing = [];
|
|
331
|
+
const results = [];
|
|
332
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
333
|
+
const executor = Promise.resolve().then(tasks[i]);
|
|
334
|
+
results.push(executor);
|
|
335
|
+
if (executing.length < limit) {
|
|
336
|
+
executing.push(executor);
|
|
337
|
+
executor.finally(() => {
|
|
338
|
+
const index = executing.indexOf(executor);
|
|
339
|
+
if (index > -1) {
|
|
340
|
+
executing.splice(index, 1);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
await Promise.race(executing);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return Promise.all(results);
|
|
349
|
+
};
|
|
350
|
+
const programAccountBufferMap = new Map();
|
|
351
|
+
// @ts-ignore
|
|
352
|
+
const chunkSize = (_a = this.syncConfig.chunkSize) !== null && _a !== void 0 ? _a : 100;
|
|
353
|
+
const tasks = [];
|
|
354
|
+
for (let i = 0; i < accountPublicKeys.length; i += chunkSize) {
|
|
355
|
+
const chunk = accountPublicKeys.slice(i, i + chunkSize);
|
|
356
|
+
tasks.push(async () => {
|
|
357
|
+
const accountInfos = await this.connection.getMultipleAccountsInfoAndContext(chunk, {
|
|
358
|
+
commitment: this.commitment,
|
|
359
|
+
});
|
|
360
|
+
const accountInfosSlot = accountInfos.context.slot;
|
|
361
|
+
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
362
|
+
const accountInfo = accountInfos.value[j];
|
|
363
|
+
if (accountInfo === null)
|
|
364
|
+
continue;
|
|
365
|
+
const publicKeyString = chunk[j].toString();
|
|
366
|
+
const buffer = buffer_1.Buffer.from(accountInfo.data);
|
|
367
|
+
programAccountBufferMap.set(publicKeyString, buffer);
|
|
368
|
+
const decodedUser = this.decode('User', buffer);
|
|
369
|
+
const currAccountWithSlot = this.getWithSlot(publicKeyString);
|
|
370
|
+
if (currAccountWithSlot &&
|
|
371
|
+
currAccountWithSlot.slot <= accountInfosSlot) {
|
|
372
|
+
this.updateUserAccount(publicKeyString, decodedUser, accountInfosSlot);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
await this.addPubkey(new web3_js_1.PublicKey(publicKeyString), decodedUser, accountInfosSlot);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
// @ts-ignore
|
|
381
|
+
const concurrencyLimit = (_b = this.syncConfig.concurrencyLimit) !== null && _b !== void 0 ? _b : 10;
|
|
382
|
+
await limitConcurrency(tasks, concurrencyLimit);
|
|
383
|
+
for (const [key] of this.entries()) {
|
|
384
|
+
if (!programAccountBufferMap.has(key)) {
|
|
385
|
+
const user = this.get(key);
|
|
386
|
+
if (user) {
|
|
387
|
+
await user.unsubscribe();
|
|
388
|
+
this.userMap.delete(key);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
console.error(`Error in UserMap.sync():`, err);
|
|
395
|
+
}
|
|
396
|
+
finally {
|
|
397
|
+
if (this.syncPromiseResolver) {
|
|
398
|
+
this.syncPromiseResolver();
|
|
399
|
+
}
|
|
400
|
+
this.syncPromise = undefined;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
301
403
|
async unsubscribe() {
|
|
302
404
|
await this.subscription.unsubscribe();
|
|
303
405
|
for (const [key, user] of this.entries()) {
|
|
@@ -3,6 +3,13 @@ import { DriftClient } from '../driftClient';
|
|
|
3
3
|
export type UserAccountFilterCriteria = {
|
|
4
4
|
hasOpenOrders: boolean;
|
|
5
5
|
};
|
|
6
|
+
export type SyncConfig = {
|
|
7
|
+
type: 'default';
|
|
8
|
+
} | {
|
|
9
|
+
type: 'paginated';
|
|
10
|
+
chunkSize?: number;
|
|
11
|
+
concurrencyLimit?: number;
|
|
12
|
+
};
|
|
6
13
|
export type UserMapConfig = {
|
|
7
14
|
driftClient: DriftClient;
|
|
8
15
|
connection?: Connection;
|
|
@@ -20,4 +27,5 @@ export type UserMapConfig = {
|
|
|
20
27
|
includeIdle?: boolean;
|
|
21
28
|
fastDecode?: boolean;
|
|
22
29
|
disableSyncOnTotalAccountsChange?: boolean;
|
|
30
|
+
syncConfig?: SyncConfig;
|
|
23
31
|
};
|
package/package.json
CHANGED
package/src/adminClient.ts
CHANGED
|
@@ -102,9 +102,11 @@ export class AdminClient extends DriftClient {
|
|
|
102
102
|
orderTickSize = ONE,
|
|
103
103
|
orderStepSize = ONE,
|
|
104
104
|
ifTotalFactor = 0,
|
|
105
|
-
name = DEFAULT_MARKET_NAME
|
|
105
|
+
name = DEFAULT_MARKET_NAME,
|
|
106
|
+
marketIndex?: number
|
|
106
107
|
): Promise<TransactionSignature> {
|
|
107
|
-
const spotMarketIndex =
|
|
108
|
+
const spotMarketIndex =
|
|
109
|
+
marketIndex ?? this.getStateAccount().numberOfSpotMarkets;
|
|
108
110
|
|
|
109
111
|
const initializeIx = await this.getInitializeSpotMarketIx(
|
|
110
112
|
mint,
|
|
@@ -127,15 +129,14 @@ export class AdminClient extends DriftClient {
|
|
|
127
129
|
orderTickSize,
|
|
128
130
|
orderStepSize,
|
|
129
131
|
ifTotalFactor,
|
|
130
|
-
name
|
|
132
|
+
name,
|
|
133
|
+
marketIndex
|
|
131
134
|
);
|
|
132
135
|
|
|
133
136
|
const tx = await this.buildTransaction(initializeIx);
|
|
134
137
|
|
|
135
138
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
136
139
|
|
|
137
|
-
// const { txSig } = await this.sendTransaction(initializeTx, [], this.opts);
|
|
138
|
-
|
|
139
140
|
await this.accountSubscriber.addSpotMarket(spotMarketIndex);
|
|
140
141
|
await this.accountSubscriber.addOracle({
|
|
141
142
|
source: oracleSource,
|
|
@@ -167,9 +168,11 @@ export class AdminClient extends DriftClient {
|
|
|
167
168
|
orderTickSize = ONE,
|
|
168
169
|
orderStepSize = ONE,
|
|
169
170
|
ifTotalFactor = 0,
|
|
170
|
-
name = DEFAULT_MARKET_NAME
|
|
171
|
+
name = DEFAULT_MARKET_NAME,
|
|
172
|
+
marketIndex?: number
|
|
171
173
|
): Promise<TransactionInstruction> {
|
|
172
|
-
const spotMarketIndex =
|
|
174
|
+
const spotMarketIndex =
|
|
175
|
+
marketIndex ?? this.getStateAccount().numberOfSpotMarkets;
|
|
173
176
|
const spotMarket = await getSpotMarketPublicKey(
|
|
174
177
|
this.program.programId,
|
|
175
178
|
spotMarketIndex
|
|
@@ -444,7 +447,7 @@ export class AdminClient extends DriftClient {
|
|
|
444
447
|
await this.fetchAccounts();
|
|
445
448
|
}
|
|
446
449
|
|
|
447
|
-
await this.accountSubscriber.addPerpMarket(
|
|
450
|
+
await this.accountSubscriber.addPerpMarket(marketIndex);
|
|
448
451
|
await this.accountSubscriber.addOracle({
|
|
449
452
|
source: oracleSource,
|
|
450
453
|
publicKey: priceOracle,
|
|
@@ -482,10 +485,9 @@ export class AdminClient extends DriftClient {
|
|
|
482
485
|
ammJitIntensity = 0,
|
|
483
486
|
name = DEFAULT_MARKET_NAME
|
|
484
487
|
): Promise<TransactionInstruction> {
|
|
485
|
-
const currentPerpMarketIndex = this.getStateAccount().numberOfMarkets;
|
|
486
488
|
const perpMarketPublicKey = await getPerpMarketPublicKey(
|
|
487
489
|
this.program.programId,
|
|
488
|
-
|
|
490
|
+
marketIndex
|
|
489
491
|
);
|
|
490
492
|
|
|
491
493
|
const nameBuffer = encodeName(name);
|
|
@@ -577,6 +577,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
577
577
|
launchTs: 1716595200000,
|
|
578
578
|
oracleSource: OracleSource.SWITCHBOARD,
|
|
579
579
|
},
|
|
580
|
+
{
|
|
581
|
+
fullName: 'Sanctum',
|
|
582
|
+
category: ['LST', 'Solana'],
|
|
583
|
+
symbol: 'CLOUD-PERP',
|
|
584
|
+
baseAssetSymbol: 'CLOUD',
|
|
585
|
+
marketIndex: 31,
|
|
586
|
+
oracle: new PublicKey('C7UxgCodaEy4yqwTe3a4QXfsG7LnpMGGQdEqaxDae4b8'),
|
|
587
|
+
launchTs: 1717597648000,
|
|
588
|
+
oracleSource: OracleSource.Prelaunch,
|
|
589
|
+
},
|
|
580
590
|
];
|
|
581
591
|
|
|
582
592
|
export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
|
package/src/dlob/DLOB.ts
CHANGED
|
@@ -76,7 +76,7 @@ type OrderBookCallback = () => void;
|
|
|
76
76
|
* Receives a DLOBNode and is expected to return true if the node should
|
|
77
77
|
* be taken into account when generating, or false otherwise.
|
|
78
78
|
*
|
|
79
|
-
* Currently used in
|
|
79
|
+
* Currently used in functions that rely on getBestNode
|
|
80
80
|
*/
|
|
81
81
|
export type DLOBFilterFcn = (node: DLOBNode) => boolean;
|
|
82
82
|
|
|
@@ -1046,7 +1046,8 @@ export class DLOB {
|
|
|
1046
1046
|
marketIndex: number,
|
|
1047
1047
|
marketType: MarketType,
|
|
1048
1048
|
slot: number,
|
|
1049
|
-
oraclePriceData: OraclePriceData
|
|
1049
|
+
oraclePriceData: OraclePriceData,
|
|
1050
|
+
filterFcn?: DLOBFilterFcn
|
|
1050
1051
|
): Generator<DLOBNode> {
|
|
1051
1052
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
1052
1053
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -1067,7 +1068,8 @@ export class DLOB {
|
|
|
1067
1068
|
slot,
|
|
1068
1069
|
(bestNode, currentNode) => {
|
|
1069
1070
|
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1070
|
-
}
|
|
1071
|
+
},
|
|
1072
|
+
filterFcn
|
|
1071
1073
|
);
|
|
1072
1074
|
}
|
|
1073
1075
|
|
|
@@ -1075,7 +1077,8 @@ export class DLOB {
|
|
|
1075
1077
|
marketIndex: number,
|
|
1076
1078
|
marketType: MarketType,
|
|
1077
1079
|
slot: number,
|
|
1078
|
-
oraclePriceData: OraclePriceData
|
|
1080
|
+
oraclePriceData: OraclePriceData,
|
|
1081
|
+
filterFcn?: DLOBFilterFcn
|
|
1079
1082
|
): Generator<DLOBNode> {
|
|
1080
1083
|
const marketTypeStr = getVariant(marketType) as MarketTypeStr;
|
|
1081
1084
|
const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
@@ -1096,7 +1099,8 @@ export class DLOB {
|
|
|
1096
1099
|
slot,
|
|
1097
1100
|
(bestNode, currentNode) => {
|
|
1098
1101
|
return bestNode.order.slot.lt(currentNode.order.slot);
|
|
1099
|
-
}
|
|
1102
|
+
},
|
|
1103
|
+
filterFcn
|
|
1100
1104
|
);
|
|
1101
1105
|
}
|
|
1102
1106
|
|
|
@@ -1241,7 +1245,8 @@ export class DLOB {
|
|
|
1241
1245
|
fallbackAsk: BN | undefined,
|
|
1242
1246
|
slot: number,
|
|
1243
1247
|
marketType: MarketType,
|
|
1244
|
-
oraclePriceData: OraclePriceData
|
|
1248
|
+
oraclePriceData: OraclePriceData,
|
|
1249
|
+
filterFcn?: DLOBFilterFcn
|
|
1245
1250
|
): Generator<DLOBNode> {
|
|
1246
1251
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
1247
1252
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
@@ -1284,7 +1289,8 @@ export class DLOB {
|
|
|
1284
1289
|
return bestNode
|
|
1285
1290
|
.getPrice(oraclePriceData, slot)
|
|
1286
1291
|
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
1287
|
-
}
|
|
1292
|
+
},
|
|
1293
|
+
filterFcn
|
|
1288
1294
|
);
|
|
1289
1295
|
}
|
|
1290
1296
|
|
|
@@ -1293,7 +1299,8 @@ export class DLOB {
|
|
|
1293
1299
|
fallbackBid: BN | undefined,
|
|
1294
1300
|
slot: number,
|
|
1295
1301
|
marketType: MarketType,
|
|
1296
|
-
oraclePriceData: OraclePriceData
|
|
1302
|
+
oraclePriceData: OraclePriceData,
|
|
1303
|
+
filterFcn?: DLOBFilterFcn
|
|
1297
1304
|
): Generator<DLOBNode> {
|
|
1298
1305
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
1299
1306
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
@@ -1336,7 +1343,8 @@ export class DLOB {
|
|
|
1336
1343
|
return bestNode
|
|
1337
1344
|
.getPrice(oraclePriceData, slot)
|
|
1338
1345
|
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1339
|
-
}
|
|
1346
|
+
},
|
|
1347
|
+
filterFcn
|
|
1340
1348
|
);
|
|
1341
1349
|
}
|
|
1342
1350
|
|
package/src/driftClient.ts
CHANGED
|
@@ -5385,7 +5385,8 @@ export class DriftClient {
|
|
|
5385
5385
|
marketIndexes: number[],
|
|
5386
5386
|
opts?: {
|
|
5387
5387
|
filterInvalidMarkets?: boolean;
|
|
5388
|
-
}
|
|
5388
|
+
},
|
|
5389
|
+
txParams?: TxParams
|
|
5389
5390
|
): Promise<TransactionSignature> {
|
|
5390
5391
|
const filterInvalidMarkets = opts?.filterInvalidMarkets;
|
|
5391
5392
|
|
|
@@ -5418,9 +5419,12 @@ export class DriftClient {
|
|
|
5418
5419
|
// # Settle filtered market indexes
|
|
5419
5420
|
const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
|
|
5420
5421
|
|
|
5421
|
-
const tx = await this.buildTransaction(
|
|
5422
|
-
|
|
5423
|
-
|
|
5422
|
+
const tx = await this.buildTransaction(
|
|
5423
|
+
ixs,
|
|
5424
|
+
txParams ?? {
|
|
5425
|
+
computeUnits: 1_400_000,
|
|
5426
|
+
}
|
|
5427
|
+
);
|
|
5424
5428
|
|
|
5425
5429
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
5426
5430
|
return txSig;
|
package/src/idl/drift.json
CHANGED
package/src/math/auction.ts
CHANGED
|
@@ -88,7 +88,7 @@ export function getAuctionPriceForOracleOffsetAuction(
|
|
|
88
88
|
const deltaNumerator = BN.min(slotsElapsed, deltaDenominator);
|
|
89
89
|
|
|
90
90
|
if (deltaDenominator.eq(ZERO)) {
|
|
91
|
-
return oraclePrice.add(order.auctionEndPrice);
|
|
91
|
+
return BN.max(oraclePrice.add(order.auctionEndPrice), ONE);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
let priceOffsetDelta;
|
|
@@ -111,7 +111,7 @@ export function getAuctionPriceForOracleOffsetAuction(
|
|
|
111
111
|
priceOffset = order.auctionStartPrice.sub(priceOffsetDelta);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
return oraclePrice.add(priceOffset);
|
|
114
|
+
return BN.max(oraclePrice.add(priceOffset), ONE);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
export function deriveOracleAuctionParams({
|
package/src/math/orders.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Order,
|
|
8
8
|
PositionDirection,
|
|
9
9
|
} from '../types';
|
|
10
|
-
import { ZERO, TWO } from '../constants/numericConstants';
|
|
10
|
+
import { ZERO, TWO, ONE } from '../constants/numericConstants';
|
|
11
11
|
import { BN } from '@coral-xyz/anchor';
|
|
12
12
|
import { OraclePriceData } from '../oracles/types';
|
|
13
13
|
import {
|
|
@@ -160,7 +160,10 @@ export function getLimitPrice(
|
|
|
160
160
|
if (hasAuctionPrice(order, slot)) {
|
|
161
161
|
limitPrice = getAuctionPrice(order, slot, oraclePriceData.price);
|
|
162
162
|
} else if (order.oraclePriceOffset !== 0) {
|
|
163
|
-
limitPrice =
|
|
163
|
+
limitPrice = BN.max(
|
|
164
|
+
oraclePriceData.price.add(new BN(order.oraclePriceOffset)),
|
|
165
|
+
ONE
|
|
166
|
+
);
|
|
164
167
|
} else if (order.price.eq(ZERO)) {
|
|
165
168
|
limitPrice = fallbackPrice;
|
|
166
169
|
} else {
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { Buffer } from 'buffer';
|
|
|
29
29
|
import { ZSTDDecoder } from 'zstddec';
|
|
30
30
|
import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
|
|
31
31
|
import {
|
|
32
|
+
SyncConfig,
|
|
32
33
|
UserAccountFilterCriteria as UserFilterCriteria,
|
|
33
34
|
UserMapConfig,
|
|
34
35
|
} from './userMapConfig';
|
|
@@ -83,6 +84,7 @@ export class UserMap implements UserMapInterface {
|
|
|
83
84
|
};
|
|
84
85
|
private decode;
|
|
85
86
|
private mostRecentSlot = 0;
|
|
87
|
+
private syncConfig: SyncConfig;
|
|
86
88
|
|
|
87
89
|
private syncPromise?: Promise<void>;
|
|
88
90
|
private syncPromiseResolver: () => void;
|
|
@@ -132,6 +134,10 @@ export class UserMap implements UserMapInterface {
|
|
|
132
134
|
decodeFn,
|
|
133
135
|
});
|
|
134
136
|
}
|
|
137
|
+
|
|
138
|
+
this.syncConfig = config.syncConfig ?? {
|
|
139
|
+
type: 'default',
|
|
140
|
+
};
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
public async subscribe() {
|
|
@@ -345,6 +351,14 @@ export class UserMap implements UserMapInterface {
|
|
|
345
351
|
}
|
|
346
352
|
|
|
347
353
|
public async sync() {
|
|
354
|
+
if (this.syncConfig.type === 'default') {
|
|
355
|
+
return this.defaultSync();
|
|
356
|
+
} else {
|
|
357
|
+
return this.paginatedSync();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private async defaultSync() {
|
|
348
362
|
if (this.syncPromise) {
|
|
349
363
|
return this.syncPromise;
|
|
350
364
|
}
|
|
@@ -438,6 +452,123 @@ export class UserMap implements UserMapInterface {
|
|
|
438
452
|
}
|
|
439
453
|
}
|
|
440
454
|
|
|
455
|
+
private async paginatedSync() {
|
|
456
|
+
if (this.syncPromise) {
|
|
457
|
+
return this.syncPromise;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.syncPromise = new Promise<void>((resolve) => {
|
|
461
|
+
this.syncPromiseResolver = resolve;
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
const accountsPrefetch = await this.connection.getProgramAccounts(
|
|
466
|
+
this.driftClient.program.programId,
|
|
467
|
+
{
|
|
468
|
+
dataSlice: { offset: 0, length: 0 },
|
|
469
|
+
filters: [
|
|
470
|
+
getUserFilter(),
|
|
471
|
+
...(!this.includeIdle ? [getNonIdleUserFilter()] : []),
|
|
472
|
+
],
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
const accountPublicKeys = accountsPrefetch.map(
|
|
476
|
+
(account) => account.pubkey
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
const limitConcurrency = async (tasks, limit) => {
|
|
480
|
+
const executing = [];
|
|
481
|
+
const results = [];
|
|
482
|
+
|
|
483
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
484
|
+
const executor = Promise.resolve().then(tasks[i]);
|
|
485
|
+
results.push(executor);
|
|
486
|
+
|
|
487
|
+
if (executing.length < limit) {
|
|
488
|
+
executing.push(executor);
|
|
489
|
+
executor.finally(() => {
|
|
490
|
+
const index = executing.indexOf(executor);
|
|
491
|
+
if (index > -1) {
|
|
492
|
+
executing.splice(index, 1);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
await Promise.race(executing);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return Promise.all(results);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const programAccountBufferMap = new Map<string, Buffer>();
|
|
504
|
+
|
|
505
|
+
// @ts-ignore
|
|
506
|
+
const chunkSize = this.syncConfig.chunkSize ?? 100;
|
|
507
|
+
const tasks = [];
|
|
508
|
+
for (let i = 0; i < accountPublicKeys.length; i += chunkSize) {
|
|
509
|
+
const chunk = accountPublicKeys.slice(i, i + chunkSize);
|
|
510
|
+
tasks.push(async () => {
|
|
511
|
+
const accountInfos =
|
|
512
|
+
await this.connection.getMultipleAccountsInfoAndContext(chunk, {
|
|
513
|
+
commitment: this.commitment,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
const accountInfosSlot = accountInfos.context.slot;
|
|
517
|
+
|
|
518
|
+
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
519
|
+
const accountInfo = accountInfos.value[j];
|
|
520
|
+
if (accountInfo === null) continue;
|
|
521
|
+
|
|
522
|
+
const publicKeyString = chunk[j].toString();
|
|
523
|
+
const buffer = Buffer.from(accountInfo.data);
|
|
524
|
+
programAccountBufferMap.set(publicKeyString, buffer);
|
|
525
|
+
|
|
526
|
+
const decodedUser = this.decode('User', buffer);
|
|
527
|
+
|
|
528
|
+
const currAccountWithSlot = this.getWithSlot(publicKeyString);
|
|
529
|
+
if (
|
|
530
|
+
currAccountWithSlot &&
|
|
531
|
+
currAccountWithSlot.slot <= accountInfosSlot
|
|
532
|
+
) {
|
|
533
|
+
this.updateUserAccount(
|
|
534
|
+
publicKeyString,
|
|
535
|
+
decodedUser,
|
|
536
|
+
accountInfosSlot
|
|
537
|
+
);
|
|
538
|
+
} else {
|
|
539
|
+
await this.addPubkey(
|
|
540
|
+
new PublicKey(publicKeyString),
|
|
541
|
+
decodedUser,
|
|
542
|
+
accountInfosSlot
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// @ts-ignore
|
|
550
|
+
const concurrencyLimit = this.syncConfig.concurrencyLimit ?? 10;
|
|
551
|
+
await limitConcurrency(tasks, concurrencyLimit);
|
|
552
|
+
|
|
553
|
+
for (const [key] of this.entries()) {
|
|
554
|
+
if (!programAccountBufferMap.has(key)) {
|
|
555
|
+
const user = this.get(key);
|
|
556
|
+
if (user) {
|
|
557
|
+
await user.unsubscribe();
|
|
558
|
+
this.userMap.delete(key);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
} catch (err) {
|
|
563
|
+
console.error(`Error in UserMap.sync():`, err);
|
|
564
|
+
} finally {
|
|
565
|
+
if (this.syncPromiseResolver) {
|
|
566
|
+
this.syncPromiseResolver();
|
|
567
|
+
}
|
|
568
|
+
this.syncPromise = undefined;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
441
572
|
public async unsubscribe() {
|
|
442
573
|
await this.subscription.unsubscribe();
|
|
443
574
|
|
|
@@ -7,6 +7,16 @@ export type UserAccountFilterCriteria = {
|
|
|
7
7
|
hasOpenOrders: boolean;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
export type SyncConfig =
|
|
11
|
+
| {
|
|
12
|
+
type: 'default';
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
type: 'paginated';
|
|
16
|
+
chunkSize?: number;
|
|
17
|
+
concurrencyLimit?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
10
20
|
export type UserMapConfig = {
|
|
11
21
|
driftClient: DriftClient;
|
|
12
22
|
// connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection
|
|
@@ -36,4 +46,6 @@ export type UserMapConfig = {
|
|
|
36
46
|
// If true, will not do a full sync whenever StateAccount.numberOfSubAccounts changes.
|
|
37
47
|
// default behavior is to do a full sync on changes.
|
|
38
48
|
disableSyncOnTotalAccountsChange?: boolean;
|
|
49
|
+
|
|
50
|
+
syncConfig?: SyncConfig;
|
|
39
51
|
};
|