@drift-labs/sdk 2.40.0-beta.9 → 2.41.0-beta.1
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/bun.lockb +0 -0
- package/lib/addresses/pda.d.ts +1 -0
- package/lib/addresses/pda.js +5 -1
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +20 -0
- package/lib/constants/spotMarkets.js +1 -1
- package/lib/dlob/DLOB.d.ts +2 -6
- package/lib/dlob/DLOB.js +9 -11
- package/lib/dlob/DLOBSubscriber.js +4 -7
- package/lib/dlob/orderBookLevels.d.ts +4 -2
- package/lib/dlob/orderBookLevels.js +79 -16
- package/lib/factory/bigNum.js +4 -2
- package/lib/idl/drift.json +171 -2
- package/lib/jupiter/jupiterClient.d.ts +3 -2
- package/lib/jupiter/jupiterClient.js +3 -2
- package/lib/math/auction.d.ts +12 -1
- package/lib/math/auction.js +22 -1
- package/lib/math/market.js +2 -4
- package/lib/math/superStake.js +1 -1
- package/lib/math/trade.js +2 -4
- package/lib/orderSubscriber/WebsocketSubscription.js +2 -0
- package/package.json +2 -1
- package/src/addresses/pda.ts +9 -0
- package/src/adminClient.ts +32 -0
- package/src/constants/spotMarkets.ts +1 -1
- package/src/dlob/DLOB.ts +9 -32
- package/src/dlob/DLOBSubscriber.ts +8 -7
- package/src/dlob/orderBookLevels.ts +133 -32
- package/src/factory/bigNum.ts +2 -0
- package/src/idl/drift.json +171 -2
- package/src/jupiter/jupiterClient.ts +4 -1
- package/src/math/auction.ts +36 -2
- package/src/math/market.ts +4 -9
- package/src/math/superStake.ts +2 -2
- package/src/math/trade.ts +3 -11
- package/src/orderSubscriber/WebsocketSubscription.ts +1 -0
- package/tests/amm/test.ts +402 -0
- package/tests/auctions/test.ts +66 -0
- package/tests/dlob/test.ts +1 -73
|
@@ -43,8 +43,8 @@ class JupiterClient {
|
|
|
43
43
|
* @param swapMode the swap mode (ExactIn or ExactOut)
|
|
44
44
|
* @param onlyDirectRoutes whether to only return direct routes
|
|
45
45
|
*/
|
|
46
|
-
async getQuote({ inputMint, outputMint, amount, maxAccounts =
|
|
47
|
-
slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, }) {
|
|
46
|
+
async getQuote({ inputMint, outputMint, amount, maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
47
|
+
slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes = [], }) {
|
|
48
48
|
const params = new URLSearchParams({
|
|
49
49
|
inputMint: inputMint.toString(),
|
|
50
50
|
outputMint: outputMint.toString(),
|
|
@@ -53,6 +53,7 @@ class JupiterClient {
|
|
|
53
53
|
swapMode,
|
|
54
54
|
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
55
55
|
maxAccounts: maxAccounts.toString(),
|
|
56
|
+
excludeDexes: excludeDexes.join(','),
|
|
56
57
|
}).toString();
|
|
57
58
|
const quote = await (await (0, node_fetch_1.default)(`${this.url}/v6/quote?${params}`)).json();
|
|
58
59
|
return quote;
|
package/lib/math/auction.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import { Order } from '../types';
|
|
1
|
+
import { Order, PositionDirection } from '../types';
|
|
2
2
|
import { BN } from '../.';
|
|
3
3
|
export declare function isAuctionComplete(order: Order, slot: number): boolean;
|
|
4
4
|
export declare function isFallbackAvailableLiquiditySource(order: Order, minAuctionDuration: number, slot: number): boolean;
|
|
5
5
|
export declare function getAuctionPrice(order: Order, slot: number, oraclePrice: BN): BN;
|
|
6
6
|
export declare function getAuctionPriceForFixedAuction(order: Order, slot: number): BN;
|
|
7
7
|
export declare function getAuctionPriceForOracleOffsetAuction(order: Order, slot: number, oraclePrice: BN): BN;
|
|
8
|
+
export declare function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }: {
|
|
9
|
+
direction: PositionDirection;
|
|
10
|
+
oraclePrice: BN;
|
|
11
|
+
auctionStartPrice: BN;
|
|
12
|
+
auctionEndPrice: BN;
|
|
13
|
+
limitPrice: BN;
|
|
14
|
+
}): {
|
|
15
|
+
auctionStartPrice: BN;
|
|
16
|
+
auctionEndPrice: BN;
|
|
17
|
+
oraclePriceOffset: number;
|
|
18
|
+
};
|
package/lib/math/auction.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAuctionPriceForOracleOffsetAuction = exports.getAuctionPriceForFixedAuction = exports.getAuctionPrice = exports.isFallbackAvailableLiquiditySource = exports.isAuctionComplete = void 0;
|
|
3
|
+
exports.deriveOracleAuctionParams = exports.getAuctionPriceForOracleOffsetAuction = exports.getAuctionPriceForFixedAuction = exports.getAuctionPrice = exports.isFallbackAvailableLiquiditySource = exports.isAuctionComplete = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const _1 = require("../.");
|
|
6
6
|
function isAuctionComplete(order, slot) {
|
|
@@ -89,3 +89,24 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
|
|
|
89
89
|
return oraclePrice.add(priceOffset);
|
|
90
90
|
}
|
|
91
91
|
exports.getAuctionPriceForOracleOffsetAuction = getAuctionPriceForOracleOffsetAuction;
|
|
92
|
+
function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }) {
|
|
93
|
+
let oraclePriceOffset = limitPrice.sub(oraclePrice);
|
|
94
|
+
if (oraclePriceOffset.eq(_1.ZERO)) {
|
|
95
|
+
oraclePriceOffset = (0, types_1.isVariant)(direction, 'long')
|
|
96
|
+
? auctionEndPrice.sub(oraclePrice).add(_1.ONE)
|
|
97
|
+
: auctionEndPrice.sub(oraclePrice).sub(_1.ONE);
|
|
98
|
+
}
|
|
99
|
+
let oraclePriceOffsetNum;
|
|
100
|
+
try {
|
|
101
|
+
oraclePriceOffsetNum = oraclePriceOffset.toNumber();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
oraclePriceOffsetNum = 0;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
auctionStartPrice: auctionStartPrice.sub(oraclePrice),
|
|
108
|
+
auctionEndPrice: auctionEndPrice.sub(oraclePrice),
|
|
109
|
+
oraclePriceOffset: oraclePriceOffsetNum,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
exports.deriveOracleAuctionParams = deriveOracleAuctionParams;
|
package/lib/math/market.js
CHANGED
|
@@ -130,12 +130,10 @@ exports.calculateNetUserPnlImbalance = calculateNetUserPnlImbalance;
|
|
|
130
130
|
function calculateAvailablePerpLiquidity(market, oraclePriceData, dlob, slot) {
|
|
131
131
|
let [bids, asks] = (0, amm_1.calculateMarketOpenBidAsk)(market.amm.baseAssetReserve, market.amm.minBaseAssetReserve, market.amm.maxBaseAssetReserve, market.amm.orderStepSize);
|
|
132
132
|
asks = asks.abs();
|
|
133
|
-
const
|
|
134
|
-
const askPrice = calculateAskPrice(market, oraclePriceData);
|
|
135
|
-
for (const bid of dlob.getMakerLimitBids(market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData, askPrice)) {
|
|
133
|
+
for (const bid of dlob.getRestingLimitBids(market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData)) {
|
|
136
134
|
bids = bids.add(bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled));
|
|
137
135
|
}
|
|
138
|
-
for (const ask of dlob.
|
|
136
|
+
for (const ask of dlob.getRestingLimitAsks(market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData)) {
|
|
139
137
|
asks = asks.add(ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled));
|
|
140
138
|
}
|
|
141
139
|
return {
|
package/lib/math/superStake.js
CHANGED
|
@@ -174,7 +174,7 @@ const getJitoSolHistoricalPriceMap = async (timestamps) => {
|
|
|
174
174
|
const jitoSolHistoricalPriceInSol = [];
|
|
175
175
|
for (let i = 0; i < data.data.getStakePoolStats.supply.length; i++) {
|
|
176
176
|
const priceInSol = data.data.getStakePoolStats.tvl[i].data /
|
|
177
|
-
|
|
177
|
+
10 ** 9 /
|
|
178
178
|
data.data.getStakePoolStats.supply[i].data;
|
|
179
179
|
jitoSolHistoricalPriceInSol.push({
|
|
180
180
|
price: priceInSol,
|
package/lib/math/trade.js
CHANGED
|
@@ -278,9 +278,7 @@ function calculateEstimatedPerpEntryPrice(assetType, amount, direction, market,
|
|
|
278
278
|
};
|
|
279
279
|
}
|
|
280
280
|
const takerIsLong = (0, types_2.isVariant)(direction, 'long');
|
|
281
|
-
const limitOrders = dlob[takerIsLong ? '
|
|
282
|
-
? (0, market_1.calculateBidPrice)(market, oraclePriceData)
|
|
283
|
-
: (0, market_1.calculateAskPrice)(market, oraclePriceData));
|
|
281
|
+
const limitOrders = dlob[takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'](market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData);
|
|
284
282
|
const swapDirection = (0, amm_1.getSwapDirection)(assetType, direction);
|
|
285
283
|
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, direction, oraclePriceData);
|
|
286
284
|
const amm = {
|
|
@@ -463,7 +461,7 @@ function calculateEstimatedSpotEntryPrice(assetType, amount, direction, market,
|
|
|
463
461
|
}
|
|
464
462
|
const basePrecision = new anchor_1.BN(Math.pow(10, market.decimals));
|
|
465
463
|
const takerIsLong = (0, types_2.isVariant)(direction, 'long');
|
|
466
|
-
const dlobLimitOrders = dlob[takerIsLong ? '
|
|
464
|
+
const dlobLimitOrders = dlob[takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'](market.marketIndex, slot, types_1.MarketType.SPOT, oraclePriceData);
|
|
467
465
|
const serumLimitOrders = takerIsLong
|
|
468
466
|
? serumAsks.getL2(100)
|
|
469
467
|
: serumBids.getL2(100);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.41.0-beta.1",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"author": "crispheaney",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"uuid": "^8.3.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
+
"@types/big.js": "^6.2.0",
|
|
47
48
|
"@types/chai": "^4.3.1",
|
|
48
49
|
"@types/jest": "^28.1.3",
|
|
49
50
|
"@types/mocha": "^9.1.1",
|
package/src/addresses/pda.ts
CHANGED
|
@@ -214,3 +214,12 @@ export function getReferrerNamePublicKeySync(
|
|
|
214
214
|
programId
|
|
215
215
|
)[0];
|
|
216
216
|
}
|
|
217
|
+
|
|
218
|
+
export function getProtocolIfSharesTransferConfigPublicKey(
|
|
219
|
+
programId: PublicKey
|
|
220
|
+
): PublicKey {
|
|
221
|
+
return PublicKey.findProgramAddressSync(
|
|
222
|
+
[Buffer.from(anchor.utils.bytes.utf8.encode('if_shares_transfer_config'))],
|
|
223
|
+
programId
|
|
224
|
+
)[0];
|
|
225
|
+
}
|
package/src/adminClient.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
getSerumOpenOrdersPublicKey,
|
|
26
26
|
getSerumFulfillmentConfigPublicKey,
|
|
27
27
|
getPhoenixFulfillmentConfigPublicKey,
|
|
28
|
+
getProtocolIfSharesTransferConfigPublicKey,
|
|
28
29
|
} from './addresses/pda';
|
|
29
30
|
import { squareRootBN } from './math/utils';
|
|
30
31
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
@@ -1516,4 +1517,35 @@ export class AdminClient extends DriftClient {
|
|
|
1516
1517
|
}
|
|
1517
1518
|
);
|
|
1518
1519
|
}
|
|
1520
|
+
|
|
1521
|
+
public async initializeProtocolIfSharesTransferConfig(): Promise<TransactionSignature> {
|
|
1522
|
+
return await this.program.rpc.initializeProtocolIfSharesTransferConfig({
|
|
1523
|
+
accounts: {
|
|
1524
|
+
admin: this.wallet.publicKey,
|
|
1525
|
+
state: await this.getStatePublicKey(),
|
|
1526
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
1527
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
1528
|
+
protocolIfSharesTransferConfig:
|
|
1529
|
+
getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
|
|
1530
|
+
},
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
public async updateProtocolIfSharesTransferConfig(
|
|
1535
|
+
whitelistedSigners?: PublicKey[],
|
|
1536
|
+
maxTransferPerEpoch?: BN
|
|
1537
|
+
): Promise<TransactionSignature> {
|
|
1538
|
+
return await this.program.rpc.updateProtocolIfSharesTransferConfig(
|
|
1539
|
+
whitelistedSigners || null,
|
|
1540
|
+
maxTransferPerEpoch,
|
|
1541
|
+
{
|
|
1542
|
+
accounts: {
|
|
1543
|
+
admin: this.wallet.publicKey,
|
|
1544
|
+
state: await this.getStatePublicKey(),
|
|
1545
|
+
protocolIfSharesTransferConfig:
|
|
1546
|
+
getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
|
|
1547
|
+
},
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1519
1551
|
}
|
|
@@ -128,7 +128,7 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
|
|
|
128
128
|
serumMarket: new PublicKey('B2na8Awyd7cpC59iEU43FagJAPLigr3AP3s38KM982bu'),
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
|
-
symbol: '
|
|
131
|
+
symbol: 'jitoSOL',
|
|
132
132
|
marketIndex: 6,
|
|
133
133
|
oracle: new PublicKey('7yyaeuJ1GGtVBLT2z2xub5ZWYKaNhF28mj1RdV4VDFVk'),
|
|
134
134
|
oracleSource: OracleSource.PYTH,
|
package/src/dlob/DLOB.ts
CHANGED
|
@@ -675,7 +675,7 @@ export class DLOB {
|
|
|
675
675
|
marketType,
|
|
676
676
|
oraclePriceData,
|
|
677
677
|
takingOrderGenerator,
|
|
678
|
-
this.
|
|
678
|
+
this.getRestingLimitBids.bind(this),
|
|
679
679
|
(takerPrice, makerPrice) => {
|
|
680
680
|
if (isVariant(marketType, 'spot')) {
|
|
681
681
|
if (takerPrice === undefined) {
|
|
@@ -687,8 +687,7 @@ export class DLOB {
|
|
|
687
687
|
}
|
|
688
688
|
}
|
|
689
689
|
return takerPrice === undefined || takerPrice.lte(makerPrice);
|
|
690
|
-
}
|
|
691
|
-
fallbackAsk
|
|
690
|
+
}
|
|
692
691
|
);
|
|
693
692
|
for (const takingAskCrossingBid of takingAsksCrossingBids) {
|
|
694
693
|
nodesToFill.push(takingAskCrossingBid);
|
|
@@ -732,7 +731,7 @@ export class DLOB {
|
|
|
732
731
|
marketType,
|
|
733
732
|
oraclePriceData,
|
|
734
733
|
takingOrderGenerator,
|
|
735
|
-
this.
|
|
734
|
+
this.getRestingLimitAsks.bind(this),
|
|
736
735
|
(takerPrice, makerPrice) => {
|
|
737
736
|
if (isVariant(marketType, 'spot')) {
|
|
738
737
|
if (takerPrice === undefined) {
|
|
@@ -745,8 +744,7 @@ export class DLOB {
|
|
|
745
744
|
}
|
|
746
745
|
|
|
747
746
|
return takerPrice === undefined || takerPrice.gte(makerPrice);
|
|
748
|
-
}
|
|
749
|
-
fallbackBid
|
|
747
|
+
}
|
|
750
748
|
);
|
|
751
749
|
|
|
752
750
|
for (const takingBidToFill of takingBidsToFill) {
|
|
@@ -790,11 +788,9 @@ export class DLOB {
|
|
|
790
788
|
marketIndex: number,
|
|
791
789
|
slot: number,
|
|
792
790
|
marketType: MarketType,
|
|
793
|
-
oraclePriceData: OraclePriceData
|
|
794
|
-
fallbackPrice?: BN
|
|
791
|
+
oraclePriceData: OraclePriceData
|
|
795
792
|
) => Generator<DLOBNode>,
|
|
796
|
-
doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
|
|
797
|
-
fallbackPrice?: BN
|
|
793
|
+
doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
|
|
798
794
|
): NodeToFill[] {
|
|
799
795
|
const nodesToFill = new Array<NodeToFill>();
|
|
800
796
|
|
|
@@ -803,8 +799,7 @@ export class DLOB {
|
|
|
803
799
|
marketIndex,
|
|
804
800
|
slot,
|
|
805
801
|
marketType,
|
|
806
|
-
oraclePriceData
|
|
807
|
-
fallbackPrice
|
|
802
|
+
oraclePriceData
|
|
808
803
|
);
|
|
809
804
|
|
|
810
805
|
for (const makerNode of makerNodeGenerator) {
|
|
@@ -1714,8 +1709,6 @@ export class DLOB {
|
|
|
1714
1709
|
* @param slot
|
|
1715
1710
|
* @param oraclePriceData
|
|
1716
1711
|
* @param depth how many levels of the order book to return
|
|
1717
|
-
* @param fallbackAsk best ask for fallback liquidity, only relevant for perps
|
|
1718
|
-
* @param fallbackBid best bid for fallback liquidity, only relevant for perps
|
|
1719
1712
|
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
1720
1713
|
*/
|
|
1721
1714
|
public getL2({
|
|
@@ -1724,8 +1717,6 @@ export class DLOB {
|
|
|
1724
1717
|
slot,
|
|
1725
1718
|
oraclePriceData,
|
|
1726
1719
|
depth,
|
|
1727
|
-
fallbackAsk,
|
|
1728
|
-
fallbackBid,
|
|
1729
1720
|
fallbackL2Generators = [],
|
|
1730
1721
|
}: {
|
|
1731
1722
|
marketIndex: number;
|
|
@@ -1733,18 +1724,10 @@ export class DLOB {
|
|
|
1733
1724
|
slot: number;
|
|
1734
1725
|
oraclePriceData: OraclePriceData;
|
|
1735
1726
|
depth: number;
|
|
1736
|
-
fallbackAsk?: BN;
|
|
1737
|
-
fallbackBid?: BN;
|
|
1738
1727
|
fallbackL2Generators?: L2OrderBookGenerator[];
|
|
1739
1728
|
}): L2OrderBook {
|
|
1740
1729
|
const makerAskL2LevelGenerator = getL2GeneratorFromDLOBNodes(
|
|
1741
|
-
this.
|
|
1742
|
-
marketIndex,
|
|
1743
|
-
slot,
|
|
1744
|
-
marketType,
|
|
1745
|
-
oraclePriceData,
|
|
1746
|
-
fallbackBid
|
|
1747
|
-
),
|
|
1730
|
+
this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
|
|
1748
1731
|
oraclePriceData,
|
|
1749
1732
|
slot
|
|
1750
1733
|
);
|
|
@@ -1765,13 +1748,7 @@ export class DLOB {
|
|
|
1765
1748
|
const asks = createL2Levels(askL2LevelGenerator, depth);
|
|
1766
1749
|
|
|
1767
1750
|
const makerBidGenerator = getL2GeneratorFromDLOBNodes(
|
|
1768
|
-
this.
|
|
1769
|
-
marketIndex,
|
|
1770
|
-
slot,
|
|
1771
|
-
marketType,
|
|
1772
|
-
oraclePriceData,
|
|
1773
|
-
fallbackAsk
|
|
1774
|
-
),
|
|
1751
|
+
this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
|
|
1775
1752
|
oraclePriceData,
|
|
1776
1753
|
slot
|
|
1777
1754
|
);
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
import { DriftClient } from '../driftClient';
|
|
11
11
|
import { isVariant, MarketType } from '../types';
|
|
12
12
|
import {
|
|
13
|
+
DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
|
|
13
14
|
getVammL2Generator,
|
|
14
15
|
L2OrderBook,
|
|
15
16
|
L2OrderBookGenerator,
|
|
16
17
|
L3OrderBook,
|
|
17
18
|
} from './orderBookLevels';
|
|
18
|
-
import { calculateAskPrice, calculateBidPrice } from '../math/market';
|
|
19
19
|
|
|
20
20
|
export class DLOBSubscriber {
|
|
21
21
|
driftClient: DriftClient;
|
|
@@ -103,8 +103,6 @@ export class DLOBSubscriber {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
let oraclePriceData;
|
|
106
|
-
let fallbackBid;
|
|
107
|
-
let fallbackAsk;
|
|
108
106
|
const isPerp = isVariant(marketType, 'perp');
|
|
109
107
|
if (isPerp) {
|
|
110
108
|
const perpMarketAccount =
|
|
@@ -112,19 +110,24 @@ export class DLOBSubscriber {
|
|
|
112
110
|
oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
|
|
113
111
|
perpMarketAccount.marketIndex
|
|
114
112
|
);
|
|
115
|
-
fallbackBid = calculateBidPrice(perpMarketAccount, oraclePriceData);
|
|
116
|
-
fallbackAsk = calculateAskPrice(perpMarketAccount, oraclePriceData);
|
|
117
113
|
} else {
|
|
118
114
|
oraclePriceData =
|
|
119
115
|
this.driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
120
116
|
}
|
|
121
117
|
|
|
122
118
|
if (isPerp && includeVamm) {
|
|
119
|
+
if (fallbackL2Generators.length > 0) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'includeVamm can only be used if fallbackL2Generators is empty'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
123
125
|
fallbackL2Generators = [
|
|
124
126
|
getVammL2Generator({
|
|
125
127
|
marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
|
|
126
128
|
oraclePriceData,
|
|
127
129
|
numOrders: numVammOrders ?? depth,
|
|
130
|
+
topOfBookQuoteAmounts: DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
|
|
128
131
|
}),
|
|
129
132
|
];
|
|
130
133
|
}
|
|
@@ -135,8 +138,6 @@ export class DLOBSubscriber {
|
|
|
135
138
|
depth,
|
|
136
139
|
oraclePriceData,
|
|
137
140
|
slot: this.slotSource.getSlot(),
|
|
138
|
-
fallbackBid,
|
|
139
|
-
fallbackAsk,
|
|
140
141
|
fallbackL2Generators: fallbackL2Generators,
|
|
141
142
|
});
|
|
142
143
|
}
|
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
OraclePriceData,
|
|
11
11
|
PerpMarketAccount,
|
|
12
12
|
PositionDirection,
|
|
13
|
+
QUOTE_PRECISION,
|
|
13
14
|
standardizePrice,
|
|
14
15
|
SwapDirection,
|
|
15
16
|
ZERO,
|
|
16
17
|
} from '..';
|
|
17
18
|
import { PublicKey } from '@solana/web3.js';
|
|
19
|
+
import { assert } from '../assert/assert';
|
|
18
20
|
|
|
19
21
|
type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix';
|
|
20
22
|
|
|
@@ -46,9 +48,16 @@ export type L3OrderBook = {
|
|
|
46
48
|
bids: L3Level[];
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
export const DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS = [
|
|
52
|
+
new BN(500).mul(QUOTE_PRECISION),
|
|
53
|
+
new BN(1000).mul(QUOTE_PRECISION),
|
|
54
|
+
new BN(2000).mul(QUOTE_PRECISION),
|
|
55
|
+
new BN(5000).mul(QUOTE_PRECISION),
|
|
56
|
+
];
|
|
57
|
+
|
|
49
58
|
/**
|
|
50
59
|
* Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
|
|
51
|
-
* @param dlobNodes e.g. {@link DLOB#
|
|
60
|
+
* @param dlobNodes e.g. {@link DLOB#getRestingLimitAsks} or {@link DLOB#getRestingLimitBids}
|
|
52
61
|
* @param oraclePriceData
|
|
53
62
|
* @param slot
|
|
54
63
|
*/
|
|
@@ -139,12 +148,20 @@ export function getVammL2Generator({
|
|
|
139
148
|
oraclePriceData,
|
|
140
149
|
numOrders,
|
|
141
150
|
now,
|
|
151
|
+
topOfBookQuoteAmounts,
|
|
142
152
|
}: {
|
|
143
153
|
marketAccount: PerpMarketAccount;
|
|
144
154
|
oraclePriceData: OraclePriceData;
|
|
145
155
|
numOrders: number;
|
|
146
156
|
now?: BN;
|
|
157
|
+
topOfBookQuoteAmounts?: BN[];
|
|
147
158
|
}): L2OrderBookGenerator {
|
|
159
|
+
let numBaseOrders = numOrders;
|
|
160
|
+
if (topOfBookQuoteAmounts) {
|
|
161
|
+
numBaseOrders = numOrders - topOfBookQuoteAmounts.length;
|
|
162
|
+
assert(topOfBookQuoteAmounts.length < numOrders);
|
|
163
|
+
}
|
|
164
|
+
|
|
148
165
|
const updatedAmm = calculateUpdatedAMM(marketAccount.amm, oraclePriceData);
|
|
149
166
|
|
|
150
167
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
@@ -162,38 +179,78 @@ export function getVammL2Generator({
|
|
|
162
179
|
);
|
|
163
180
|
|
|
164
181
|
let numBids = 0;
|
|
165
|
-
|
|
182
|
+
|
|
183
|
+
let topOfBookBidSize = ZERO;
|
|
184
|
+
let bidSize = openBids.div(new BN(numBaseOrders));
|
|
166
185
|
const bidAmm = {
|
|
167
186
|
baseAssetReserve: bidReserves.baseAssetReserve,
|
|
168
187
|
quoteAssetReserve: bidReserves.quoteAssetReserve,
|
|
169
188
|
sqrtK: updatedAmm.sqrtK,
|
|
170
189
|
pegMultiplier: updatedAmm.pegMultiplier,
|
|
171
190
|
};
|
|
191
|
+
|
|
172
192
|
const getL2Bids = function* () {
|
|
173
|
-
while (numBids < numOrders &&
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
193
|
+
while (numBids < numOrders && bidSize.gt(ZERO)) {
|
|
194
|
+
let quoteSwapped = ZERO;
|
|
195
|
+
let baseSwapped = ZERO;
|
|
196
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
|
|
197
|
+
|
|
198
|
+
if (topOfBookQuoteAmounts && numBids < topOfBookQuoteAmounts?.length) {
|
|
199
|
+
const remainingBaseLiquidity = openBids.sub(topOfBookBidSize);
|
|
200
|
+
quoteSwapped = topOfBookQuoteAmounts[numBids];
|
|
201
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
202
|
+
calculateAmmReservesAfterSwap(
|
|
203
|
+
bidAmm,
|
|
204
|
+
'quote',
|
|
205
|
+
quoteSwapped,
|
|
206
|
+
SwapDirection.REMOVE
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
baseSwapped = bidAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
210
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
211
|
+
baseSwapped = remainingBaseLiquidity;
|
|
212
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
213
|
+
calculateAmmReservesAfterSwap(
|
|
214
|
+
bidAmm,
|
|
215
|
+
'base',
|
|
216
|
+
baseSwapped,
|
|
217
|
+
SwapDirection.ADD
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
221
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
222
|
+
bidAmm.pegMultiplier,
|
|
223
|
+
SwapDirection.ADD
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
topOfBookBidSize = topOfBookBidSize.add(baseSwapped);
|
|
227
|
+
bidSize = openBids.sub(topOfBookBidSize).div(new BN(numBaseOrders));
|
|
228
|
+
} else {
|
|
229
|
+
baseSwapped = bidSize;
|
|
230
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
231
|
+
calculateAmmReservesAfterSwap(
|
|
232
|
+
bidAmm,
|
|
233
|
+
'base',
|
|
234
|
+
baseSwapped,
|
|
235
|
+
SwapDirection.ADD
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
239
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
240
|
+
bidAmm.pegMultiplier,
|
|
179
241
|
SwapDirection.ADD
|
|
180
242
|
);
|
|
243
|
+
}
|
|
181
244
|
|
|
182
|
-
const
|
|
183
|
-
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
184
|
-
bidAmm.pegMultiplier,
|
|
185
|
-
SwapDirection.ADD
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const price = quoteSwapped.mul(BASE_PRECISION).div(baseSize);
|
|
245
|
+
const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
|
|
189
246
|
|
|
190
247
|
bidAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
191
248
|
bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
192
249
|
|
|
193
250
|
yield {
|
|
194
251
|
price,
|
|
195
|
-
size:
|
|
196
|
-
sources: { vamm:
|
|
252
|
+
size: baseSwapped,
|
|
253
|
+
sources: { vamm: baseSwapped },
|
|
197
254
|
};
|
|
198
255
|
|
|
199
256
|
numBids++;
|
|
@@ -201,38 +258,82 @@ export function getVammL2Generator({
|
|
|
201
258
|
};
|
|
202
259
|
|
|
203
260
|
let numAsks = 0;
|
|
204
|
-
|
|
261
|
+
let topOfBookAskSize = ZERO;
|
|
262
|
+
let askSize = openAsks.abs().div(new BN(numBaseOrders));
|
|
205
263
|
const askAmm = {
|
|
206
264
|
baseAssetReserve: askReserves.baseAssetReserve,
|
|
207
265
|
quoteAssetReserve: askReserves.quoteAssetReserve,
|
|
208
266
|
sqrtK: updatedAmm.sqrtK,
|
|
209
267
|
pegMultiplier: updatedAmm.pegMultiplier,
|
|
210
268
|
};
|
|
269
|
+
|
|
211
270
|
const getL2Asks = function* () {
|
|
212
271
|
while (numAsks < numOrders && askSize.gt(ZERO)) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
272
|
+
let quoteSwapped: BN = ZERO;
|
|
273
|
+
let baseSwapped: BN = ZERO;
|
|
274
|
+
let [afterSwapQuoteReserves, afterSwapBaseReserves] = [ZERO, ZERO];
|
|
275
|
+
|
|
276
|
+
if (topOfBookQuoteAmounts && numAsks < topOfBookQuoteAmounts?.length) {
|
|
277
|
+
const remainingBaseLiquidity = openAsks
|
|
278
|
+
.mul(new BN(-1))
|
|
279
|
+
.sub(topOfBookAskSize);
|
|
280
|
+
quoteSwapped = topOfBookQuoteAmounts[numAsks];
|
|
281
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
282
|
+
calculateAmmReservesAfterSwap(
|
|
283
|
+
askAmm,
|
|
284
|
+
'quote',
|
|
285
|
+
quoteSwapped,
|
|
286
|
+
SwapDirection.ADD
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
baseSwapped = askAmm.baseAssetReserve.sub(afterSwapBaseReserves).abs();
|
|
290
|
+
if (remainingBaseLiquidity.lt(baseSwapped)) {
|
|
291
|
+
baseSwapped = remainingBaseLiquidity;
|
|
292
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
293
|
+
calculateAmmReservesAfterSwap(
|
|
294
|
+
bidAmm,
|
|
295
|
+
'base',
|
|
296
|
+
baseSwapped,
|
|
297
|
+
SwapDirection.REMOVE
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
301
|
+
bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
302
|
+
bidAmm.pegMultiplier,
|
|
303
|
+
SwapDirection.REMOVE
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
topOfBookAskSize = topOfBookAskSize.add(baseSwapped);
|
|
307
|
+
askSize = openAsks
|
|
308
|
+
.abs()
|
|
309
|
+
.sub(topOfBookAskSize)
|
|
310
|
+
.div(new BN(numBaseOrders));
|
|
311
|
+
} else {
|
|
312
|
+
baseSwapped = askSize;
|
|
313
|
+
[afterSwapQuoteReserves, afterSwapBaseReserves] =
|
|
314
|
+
calculateAmmReservesAfterSwap(
|
|
315
|
+
askAmm,
|
|
316
|
+
'base',
|
|
317
|
+
askSize,
|
|
318
|
+
SwapDirection.REMOVE
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
quoteSwapped = calculateQuoteAssetAmountSwapped(
|
|
322
|
+
askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
323
|
+
askAmm.pegMultiplier,
|
|
218
324
|
SwapDirection.REMOVE
|
|
219
325
|
);
|
|
326
|
+
}
|
|
220
327
|
|
|
221
|
-
const
|
|
222
|
-
askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(),
|
|
223
|
-
askAmm.pegMultiplier,
|
|
224
|
-
SwapDirection.REMOVE
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const price = quoteSwapped.mul(BASE_PRECISION).div(askSize);
|
|
328
|
+
const price = quoteSwapped.mul(BASE_PRECISION).div(baseSwapped);
|
|
228
329
|
|
|
229
330
|
askAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
230
331
|
askAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
231
332
|
|
|
232
333
|
yield {
|
|
233
334
|
price,
|
|
234
|
-
size:
|
|
235
|
-
sources: { vamm:
|
|
335
|
+
size: baseSwapped,
|
|
336
|
+
sources: { vamm: baseSwapped },
|
|
236
337
|
};
|
|
237
338
|
|
|
238
339
|
numAsks++;
|
package/src/factory/bigNum.ts
CHANGED
|
@@ -618,6 +618,8 @@ export class BigNum {
|
|
|
618
618
|
if (!val.replace(BigNum.delim, '')) {
|
|
619
619
|
return BigNum.from(ZERO, precisionShift);
|
|
620
620
|
}
|
|
621
|
+
if (val.includes('e'))
|
|
622
|
+
val = (+val).toFixed(precisionShift?.toNumber() ?? 9); // prevent small numbers e.g. 3.1e-8, use assume max precision 9 as default
|
|
621
623
|
|
|
622
624
|
const sides = val.split(BigNum.delim);
|
|
623
625
|
const rightSide = sides[1];
|