@drift-labs/sdk 2.18.0 → 2.20.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/constants/perpMarkets.js +13 -3
- package/lib/dlob/DLOB.js +19 -6
- package/lib/dlob/NodeList.d.ts +1 -1
- package/lib/dlob/NodeList.js +2 -2
- package/lib/driftClient.d.ts +2 -2
- package/lib/driftClient.js +25 -17
- package/lib/factory/oracleClient.js +4 -1
- package/lib/idl/drift.json +58 -4
- package/lib/math/amm.js +10 -15
- package/lib/math/trade.js +1 -1
- package/lib/serum/serumSubscriber.d.ts +4 -3
- package/lib/serum/serumSubscriber.js +40 -11
- package/lib/serum/types.d.ts +2 -0
- package/lib/types.d.ts +6 -3
- package/lib/types.js +2 -1
- package/package.json +1 -1
- package/src/constants/perpMarkets.ts +13 -3
- package/src/dlob/DLOB.ts +17 -5
- package/src/dlob/NodeList.ts +2 -5
- package/src/driftClient.ts +29 -20
- package/src/factory/oracleClient.ts +5 -1
- package/src/idl/drift.json +58 -4
- package/src/math/amm.ts +22 -23
- package/src/math/trade.ts +1 -1
- package/src/serum/serumSubscriber.ts +62 -20
- package/src/serum/types.ts +9 -5
- package/src/types.ts +3 -2
- package/tests/amm/test.ts +177 -5
- package/tests/dlob/test.ts +2 -1
|
@@ -47,12 +47,12 @@ exports.DevnetPerpMarkets = [
|
|
|
47
47
|
{
|
|
48
48
|
fullName: 'Bonk',
|
|
49
49
|
category: ['Meme'],
|
|
50
|
-
symbol: '
|
|
51
|
-
baseAssetSymbol: '
|
|
50
|
+
symbol: '1MBONK-PERP',
|
|
51
|
+
baseAssetSymbol: '1MBONK',
|
|
52
52
|
marketIndex: 4,
|
|
53
53
|
oracle: new web3_js_1.PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
|
|
54
54
|
launchTs: 1677068931000,
|
|
55
|
-
oracleSource: __1.OracleSource.
|
|
55
|
+
oracleSource: __1.OracleSource.PYTH_1M,
|
|
56
56
|
},
|
|
57
57
|
];
|
|
58
58
|
exports.MainnetPerpMarkets = [
|
|
@@ -96,6 +96,16 @@ exports.MainnetPerpMarkets = [
|
|
|
96
96
|
launchTs: 1675802661000,
|
|
97
97
|
oracleSource: __1.OracleSource.PYTH,
|
|
98
98
|
},
|
|
99
|
+
{
|
|
100
|
+
fullName: 'Bonk',
|
|
101
|
+
category: ['Meme'],
|
|
102
|
+
symbol: '1MBONK-PERP',
|
|
103
|
+
baseAssetSymbol: '1MBONK',
|
|
104
|
+
marketIndex: 4,
|
|
105
|
+
oracle: new web3_js_1.PublicKey('8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN'),
|
|
106
|
+
launchTs: 1677690149000,
|
|
107
|
+
oracleSource: __1.OracleSource.PYTH_1M,
|
|
108
|
+
},
|
|
99
109
|
];
|
|
100
110
|
exports.PerpMarkets = {
|
|
101
111
|
devnet: exports.DevnetPerpMarkets,
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -286,8 +286,9 @@ class DLOB {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
getOrder(orderId, userAccount) {
|
|
289
|
+
const orderSignature = NodeList_1.getOrderSignature(orderId, userAccount);
|
|
289
290
|
for (const nodeList of this.getNodeLists()) {
|
|
290
|
-
const node = nodeList.get(
|
|
291
|
+
const node = nodeList.get(orderSignature);
|
|
291
292
|
if (node) {
|
|
292
293
|
return node.order;
|
|
293
294
|
}
|
|
@@ -761,20 +762,32 @@ class DLOB {
|
|
|
761
762
|
determineMakerAndTaker(askNode, bidNode) {
|
|
762
763
|
const askSlot = askNode.order.slot.add(new __1.BN(askNode.order.auctionDuration));
|
|
763
764
|
const bidSlot = bidNode.order.slot.add(new __1.BN(bidNode.order.auctionDuration));
|
|
764
|
-
if (
|
|
765
|
+
if (bidNode.order.postOnly && askNode.order.postOnly) {
|
|
766
|
+
return undefined;
|
|
767
|
+
}
|
|
768
|
+
else if (bidNode.order.postOnly) {
|
|
769
|
+
return {
|
|
770
|
+
takerNode: askNode,
|
|
771
|
+
makerNode: bidNode,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
else if (askNode.order.postOnly) {
|
|
765
775
|
return {
|
|
766
776
|
takerNode: bidNode,
|
|
767
777
|
makerNode: askNode,
|
|
768
778
|
};
|
|
769
779
|
}
|
|
770
|
-
else if (
|
|
780
|
+
else if (askSlot.lte(bidSlot)) {
|
|
771
781
|
return {
|
|
772
|
-
takerNode:
|
|
773
|
-
makerNode:
|
|
782
|
+
takerNode: bidNode,
|
|
783
|
+
makerNode: askNode,
|
|
774
784
|
};
|
|
775
785
|
}
|
|
776
786
|
else {
|
|
777
|
-
return
|
|
787
|
+
return {
|
|
788
|
+
takerNode: askNode,
|
|
789
|
+
makerNode: bidNode,
|
|
790
|
+
};
|
|
778
791
|
}
|
|
779
792
|
}
|
|
780
793
|
getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
|
package/lib/dlob/NodeList.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare class NodeList<NodeType extends keyof DLOBNodeMap> implements DLO
|
|
|
21
21
|
remove(order: Order, userAccount: PublicKey): void;
|
|
22
22
|
getGenerator(): Generator<DLOBNode>;
|
|
23
23
|
has(order: Order, userAccount: PublicKey): boolean;
|
|
24
|
-
get(
|
|
24
|
+
get(orderSignature: string): DLOBNodeMap[NodeType] | undefined;
|
|
25
25
|
print(): void;
|
|
26
26
|
printTop(): void;
|
|
27
27
|
}
|
package/lib/dlob/NodeList.js
CHANGED
|
@@ -104,8 +104,8 @@ class NodeList {
|
|
|
104
104
|
has(order, userAccount) {
|
|
105
105
|
return this.nodeMap.has(getOrderSignature(order.orderId, userAccount));
|
|
106
106
|
}
|
|
107
|
-
get(
|
|
108
|
-
return this.nodeMap.get(
|
|
107
|
+
get(orderSignature) {
|
|
108
|
+
return this.nodeMap.get(orderSignature);
|
|
109
109
|
}
|
|
110
110
|
print() {
|
|
111
111
|
let currentNode = this.head;
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -186,8 +186,8 @@ export declare class DriftClient {
|
|
|
186
186
|
getCancelOrderByUserIdIx(userOrderId: number): Promise<TransactionInstruction>;
|
|
187
187
|
cancelOrders(marketType?: MarketType, marketIndex?: number, direction?: PositionDirection, txParams?: TxParams): Promise<TransactionSignature>;
|
|
188
188
|
getCancelOrdersIx(marketType: MarketType | null, marketIndex: number | null, direction: PositionDirection | null): Promise<TransactionInstruction>;
|
|
189
|
-
fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
|
|
190
|
-
getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
|
|
189
|
+
fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
|
|
190
|
+
getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
|
|
191
191
|
placeSpotOrder(orderParams: OptionalOrderParams, txParams?: TxParams): Promise<TransactionSignature>;
|
|
192
192
|
getPlaceSpotOrderIx(orderParams: OptionalOrderParams): Promise<TransactionInstruction>;
|
|
193
193
|
fillSpotOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Order, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
|
package/lib/driftClient.js
CHANGED
|
@@ -1261,41 +1261,49 @@ class DriftClient {
|
|
|
1261
1261
|
const marketIndex = order
|
|
1262
1262
|
? order.marketIndex
|
|
1263
1263
|
: userAccount.orders.find((order) => order.orderId === userAccount.nextOrderId - 1).marketIndex;
|
|
1264
|
+
makerInfo = Array.isArray(makerInfo)
|
|
1265
|
+
? makerInfo
|
|
1266
|
+
: makerInfo
|
|
1267
|
+
? [makerInfo]
|
|
1268
|
+
: [];
|
|
1264
1269
|
const userAccounts = [userAccount];
|
|
1265
|
-
|
|
1266
|
-
userAccounts.push(
|
|
1270
|
+
for (const maker of makerInfo) {
|
|
1271
|
+
userAccounts.push(maker.makerUserAccount);
|
|
1267
1272
|
}
|
|
1268
1273
|
const remainingAccounts = this.getRemainingAccounts({
|
|
1269
1274
|
userAccounts,
|
|
1270
1275
|
writablePerpMarketIndexes: [marketIndex],
|
|
1271
1276
|
});
|
|
1272
|
-
|
|
1277
|
+
for (const maker of makerInfo) {
|
|
1273
1278
|
remainingAccounts.push({
|
|
1274
|
-
pubkey:
|
|
1279
|
+
pubkey: maker.maker,
|
|
1275
1280
|
isWritable: true,
|
|
1276
1281
|
isSigner: false,
|
|
1277
1282
|
});
|
|
1278
1283
|
remainingAccounts.push({
|
|
1279
|
-
pubkey:
|
|
1284
|
+
pubkey: maker.makerStats,
|
|
1280
1285
|
isWritable: true,
|
|
1281
1286
|
isSigner: false,
|
|
1282
1287
|
});
|
|
1283
1288
|
}
|
|
1284
1289
|
if (referrerInfo) {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1290
|
+
const referrerIsMaker = makerInfo.find((maker) => maker.maker.equals(referrerInfo.referrer)) !==
|
|
1291
|
+
undefined;
|
|
1292
|
+
if (!referrerIsMaker) {
|
|
1293
|
+
remainingAccounts.push({
|
|
1294
|
+
pubkey: referrerInfo.referrer,
|
|
1295
|
+
isWritable: true,
|
|
1296
|
+
isSigner: false,
|
|
1297
|
+
});
|
|
1298
|
+
remainingAccounts.push({
|
|
1299
|
+
pubkey: referrerInfo.referrerStats,
|
|
1300
|
+
isWritable: true,
|
|
1301
|
+
isSigner: false,
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1295
1304
|
}
|
|
1296
1305
|
const orderId = order.orderId;
|
|
1297
|
-
|
|
1298
|
-
return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
|
|
1306
|
+
return await this.program.instruction.fillPerpOrder(orderId, null, {
|
|
1299
1307
|
accounts: {
|
|
1300
1308
|
state: await this.getStatePublicKey(),
|
|
1301
1309
|
filler: fillerPublicKey,
|
|
@@ -10,9 +10,12 @@ function getOracleClient(oracleSource, connection) {
|
|
|
10
10
|
if (types_1.isVariant(oracleSource, 'pyth')) {
|
|
11
11
|
return new pythClient_1.PythClient(connection);
|
|
12
12
|
}
|
|
13
|
-
if (types_1.isVariant(oracleSource, '
|
|
13
|
+
if (types_1.isVariant(oracleSource, 'pyth1K')) {
|
|
14
14
|
return new pythClient_1.PythClient(connection, new anchor_1.BN(1000));
|
|
15
15
|
}
|
|
16
|
+
if (types_1.isVariant(oracleSource, 'pyth1M')) {
|
|
17
|
+
return new pythClient_1.PythClient(connection, new anchor_1.BN(1000000));
|
|
18
|
+
}
|
|
16
19
|
// if (isVariant(oracleSource, 'switchboard')) {
|
|
17
20
|
// return new SwitchboardClient(connection);
|
|
18
21
|
// }
|
package/lib/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.20.0-beta.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -6207,6 +6207,7 @@
|
|
|
6207
6207
|
{
|
|
6208
6208
|
"name": "Match",
|
|
6209
6209
|
"fields": [
|
|
6210
|
+
"publicKey",
|
|
6210
6211
|
{
|
|
6211
6212
|
"defined": "usize"
|
|
6212
6213
|
}
|
|
@@ -6237,14 +6238,17 @@
|
|
|
6237
6238
|
{
|
|
6238
6239
|
"name": "Pyth"
|
|
6239
6240
|
},
|
|
6240
|
-
{
|
|
6241
|
-
"name": "Pyth1000"
|
|
6242
|
-
},
|
|
6243
6241
|
{
|
|
6244
6242
|
"name": "Switchboard"
|
|
6245
6243
|
},
|
|
6246
6244
|
{
|
|
6247
6245
|
"name": "QuoteAsset"
|
|
6246
|
+
},
|
|
6247
|
+
{
|
|
6248
|
+
"name": "Pyth1K"
|
|
6249
|
+
},
|
|
6250
|
+
{
|
|
6251
|
+
"name": "Pyth1M"
|
|
6248
6252
|
}
|
|
6249
6253
|
]
|
|
6250
6254
|
}
|
|
@@ -8601,6 +8605,56 @@
|
|
|
8601
8605
|
"code": 6227,
|
|
8602
8606
|
"name": "TierViolationLiquidatingPerpPnl",
|
|
8603
8607
|
"msg": "TierViolationLiquidatingPerpPnl"
|
|
8608
|
+
},
|
|
8609
|
+
{
|
|
8610
|
+
"code": 6228,
|
|
8611
|
+
"name": "CouldNotLoadUserData",
|
|
8612
|
+
"msg": "CouldNotLoadUserData"
|
|
8613
|
+
},
|
|
8614
|
+
{
|
|
8615
|
+
"code": 6229,
|
|
8616
|
+
"name": "UserWrongMutability",
|
|
8617
|
+
"msg": "UserWrongMutability"
|
|
8618
|
+
},
|
|
8619
|
+
{
|
|
8620
|
+
"code": 6230,
|
|
8621
|
+
"name": "InvalidUserAccount",
|
|
8622
|
+
"msg": "InvalidUserAccount"
|
|
8623
|
+
},
|
|
8624
|
+
{
|
|
8625
|
+
"code": 6231,
|
|
8626
|
+
"name": "CouldNotLoadUserStatsData",
|
|
8627
|
+
"msg": "CouldNotLoadUserData"
|
|
8628
|
+
},
|
|
8629
|
+
{
|
|
8630
|
+
"code": 6232,
|
|
8631
|
+
"name": "UserStatsWrongMutability",
|
|
8632
|
+
"msg": "UserWrongMutability"
|
|
8633
|
+
},
|
|
8634
|
+
{
|
|
8635
|
+
"code": 6233,
|
|
8636
|
+
"name": "InvalidUserStatsAccount",
|
|
8637
|
+
"msg": "InvalidUserAccount"
|
|
8638
|
+
},
|
|
8639
|
+
{
|
|
8640
|
+
"code": 6234,
|
|
8641
|
+
"name": "UserNotFound",
|
|
8642
|
+
"msg": "UserNotFound"
|
|
8643
|
+
},
|
|
8644
|
+
{
|
|
8645
|
+
"code": 6235,
|
|
8646
|
+
"name": "UnableToLoadUserAccount",
|
|
8647
|
+
"msg": "UnableToLoadUserAccount"
|
|
8648
|
+
},
|
|
8649
|
+
{
|
|
8650
|
+
"code": 6236,
|
|
8651
|
+
"name": "UserStatsNotFound",
|
|
8652
|
+
"msg": "UserStatsNotFound"
|
|
8653
|
+
},
|
|
8654
|
+
{
|
|
8655
|
+
"code": 6237,
|
|
8656
|
+
"name": "UnableToLoadUserStatsAccount",
|
|
8657
|
+
"msg": "UnableToLoadUserStatsAccount"
|
|
8604
8658
|
}
|
|
8605
8659
|
]
|
|
8606
8660
|
}
|
package/lib/math/amm.js
CHANGED
|
@@ -203,26 +203,21 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
|
|
|
203
203
|
exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
|
|
204
204
|
function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
|
|
205
205
|
if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
|
|
206
|
-
return
|
|
206
|
+
return 1;
|
|
207
207
|
}
|
|
208
|
-
const
|
|
208
|
+
const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
|
|
209
209
|
// inventory skew
|
|
210
210
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
|
|
211
|
-
const minSideLiquidity = anchor_1.BN.
|
|
212
|
-
const
|
|
211
|
+
const minSideLiquidity = anchor_1.BN.min(openBids.abs(), openAsks.abs());
|
|
212
|
+
const inventoryScaleBN = anchor_1.BN.min(baseAssetAmountWithAmm
|
|
213
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
214
|
+
.div(anchor_1.BN.max(minSideLiquidity, numericConstants_1.ONE))
|
|
215
|
+
.abs(), numericConstants_1.PERCENTAGE_PRECISION);
|
|
216
|
+
const inventoryScaleMaxBN = anchor_1.BN.max(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, new anchor_1.BN(maxSpread)
|
|
213
217
|
.mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
|
|
214
218
|
.div(new anchor_1.BN(Math.max(directionalSpread, 1))));
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
.div(numericConstants_1.BASE_PRECISION)
|
|
218
|
-
.mul(defaultLargeBidAskFactor)
|
|
219
|
-
.div(minSideLiquidity)
|
|
220
|
-
.abs();
|
|
221
|
-
const inventoryScale = anchor_1.BN.min(inventoryScaleMaxBN, inventoryScaleBN).toNumber() /
|
|
222
|
-
numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
|
|
223
|
-
const inventoryScaleMax = inventoryScaleMaxBN.toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
|
|
224
|
-
const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
|
|
225
|
-
return inventorySpreadScale;
|
|
219
|
+
const inventoryScaleCapped = anchor_1.BN.min(inventoryScaleMaxBN, numericConstants_1.BID_ASK_SPREAD_PRECISION.add(inventoryScaleMaxBN.mul(inventoryScaleBN).div(numericConstants_1.PERCENTAGE_PRECISION))).toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
|
|
220
|
+
return inventoryScaleCapped;
|
|
226
221
|
}
|
|
227
222
|
exports.calculateInventoryScale = calculateInventoryScale;
|
|
228
223
|
function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
|
package/lib/math/trade.js
CHANGED
|
@@ -499,7 +499,7 @@ function calculateEstimatedSpotEntryPrice(assetType, amount, direction, market,
|
|
|
499
499
|
.div(basePrecision);
|
|
500
500
|
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
501
501
|
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
502
|
-
worstPrice =
|
|
502
|
+
worstPrice = dlobLimitOrderPrice;
|
|
503
503
|
dlobLimitOrder = dlobLimitOrders.next().value;
|
|
504
504
|
}
|
|
505
505
|
else {
|
|
@@ -8,16 +8,17 @@ export declare class SerumSubscriber {
|
|
|
8
8
|
connection: Connection;
|
|
9
9
|
programId: PublicKey;
|
|
10
10
|
marketAddress: PublicKey;
|
|
11
|
-
|
|
11
|
+
subscriptionType: 'polling' | 'websocket';
|
|
12
|
+
accountLoader: BulkAccountLoader | undefined;
|
|
12
13
|
market: Market;
|
|
13
14
|
subscribed: boolean;
|
|
14
15
|
asksAddress: PublicKey;
|
|
15
16
|
asks: Orderbook;
|
|
16
|
-
asksCallbackId: string;
|
|
17
|
+
asksCallbackId: string | number;
|
|
17
18
|
lastAsksSlot: number;
|
|
18
19
|
bidsAddress: PublicKey;
|
|
19
20
|
bids: Orderbook;
|
|
20
|
-
bidsCallbackId: string;
|
|
21
|
+
bidsCallbackId: string | number;
|
|
21
22
|
lastBidsSlot: number;
|
|
22
23
|
constructor(config: SerumMarketSubscriberConfig);
|
|
23
24
|
subscribe(): Promise<void>;
|
|
@@ -9,7 +9,13 @@ class SerumSubscriber {
|
|
|
9
9
|
this.connection = config.connection;
|
|
10
10
|
this.programId = config.programId;
|
|
11
11
|
this.marketAddress = config.marketAddress;
|
|
12
|
-
|
|
12
|
+
if (config.accountSubscription.type === 'polling') {
|
|
13
|
+
this.subscriptionType = 'polling';
|
|
14
|
+
this.accountLoader = config.accountSubscription.accountLoader;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
this.subscriptionType = 'websocket';
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
async subscribe() {
|
|
15
21
|
if (this.subscribed) {
|
|
@@ -18,16 +24,32 @@ class SerumSubscriber {
|
|
|
18
24
|
this.market = await serum_1.Market.load(this.connection, this.marketAddress, undefined, this.programId);
|
|
19
25
|
this.asksAddress = this.market.asksAddress;
|
|
20
26
|
this.asks = await this.market.loadAsks(this.connection);
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
if (this.subscriptionType === 'websocket') {
|
|
28
|
+
this.asksCallbackId = this.connection.onAccountChange(this.asksAddress, (accountInfo, ctx) => {
|
|
29
|
+
this.lastAsksSlot = ctx.slot;
|
|
30
|
+
this.asks = serum_1.Orderbook.decode(this.market, accountInfo.data);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.asksCallbackId = await this.accountLoader.addAccount(this.asksAddress, (buffer, slot) => {
|
|
35
|
+
this.lastAsksSlot = slot;
|
|
36
|
+
this.asks = serum_1.Orderbook.decode(this.market, buffer);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
25
39
|
this.bidsAddress = this.market.bidsAddress;
|
|
26
40
|
this.bids = await this.market.loadBids(this.connection);
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (this.subscriptionType === 'websocket') {
|
|
42
|
+
this.bidsCallbackId = this.connection.onAccountChange(this.bidsAddress, (accountInfo, ctx) => {
|
|
43
|
+
this.lastBidsSlot = ctx.slot;
|
|
44
|
+
this.bids = serum_1.Orderbook.decode(this.market, accountInfo.data);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.bidsCallbackId = await this.accountLoader.addAccount(this.bidsAddress, (buffer, slot) => {
|
|
49
|
+
this.lastBidsSlot = slot;
|
|
50
|
+
this.bids = serum_1.Orderbook.decode(this.market, buffer);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
31
53
|
this.subscribed = true;
|
|
32
54
|
}
|
|
33
55
|
getBestBid() {
|
|
@@ -48,8 +70,15 @@ class SerumSubscriber {
|
|
|
48
70
|
if (!this.subscribed) {
|
|
49
71
|
return;
|
|
50
72
|
}
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
// remove listeners
|
|
74
|
+
if (this.subscriptionType === 'websocket') {
|
|
75
|
+
await this.connection.removeAccountChangeListener(this.asksCallbackId);
|
|
76
|
+
await this.connection.removeAccountChangeListener(this.bidsCallbackId);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
this.accountLoader.removeAccount(this.asksAddress, this.asksCallbackId);
|
|
80
|
+
this.accountLoader.removeAccount(this.bidsAddress, this.bidsCallbackId);
|
|
81
|
+
}
|
|
53
82
|
this.subscribed = false;
|
|
54
83
|
}
|
|
55
84
|
}
|
package/lib/serum/types.d.ts
CHANGED
package/lib/types.d.ts
CHANGED
|
@@ -130,8 +130,11 @@ export declare class OracleSource {
|
|
|
130
130
|
static readonly PYTH: {
|
|
131
131
|
pyth: {};
|
|
132
132
|
};
|
|
133
|
-
static readonly
|
|
134
|
-
|
|
133
|
+
static readonly PYTH_1K: {
|
|
134
|
+
pyth1K: {};
|
|
135
|
+
};
|
|
136
|
+
static readonly PYTH_1M: {
|
|
137
|
+
pyth1M: {};
|
|
135
138
|
};
|
|
136
139
|
static readonly QUOTE_ASSET: {
|
|
137
140
|
quoteAsset: {};
|
|
@@ -925,7 +928,7 @@ export declare type MakerInfo = {
|
|
|
925
928
|
maker: PublicKey;
|
|
926
929
|
makerStats: PublicKey;
|
|
927
930
|
makerUserAccount: UserAccount;
|
|
928
|
-
order
|
|
931
|
+
order?: Order;
|
|
929
932
|
};
|
|
930
933
|
export declare type TakerInfo = {
|
|
931
934
|
taker: PublicKey;
|
package/lib/types.js
CHANGED
|
@@ -78,7 +78,8 @@ class OracleSource {
|
|
|
78
78
|
}
|
|
79
79
|
exports.OracleSource = OracleSource;
|
|
80
80
|
OracleSource.PYTH = { pyth: {} };
|
|
81
|
-
OracleSource.
|
|
81
|
+
OracleSource.PYTH_1K = { pyth1K: {} };
|
|
82
|
+
OracleSource.PYTH_1M = { pyth1M: {} };
|
|
82
83
|
// static readonly SWITCHBOARD = { switchboard: {} };
|
|
83
84
|
OracleSource.QUOTE_ASSET = { quoteAsset: {} };
|
|
84
85
|
class OrderType {
|
package/package.json
CHANGED
|
@@ -57,12 +57,12 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
57
57
|
{
|
|
58
58
|
fullName: 'Bonk',
|
|
59
59
|
category: ['Meme'],
|
|
60
|
-
symbol: '
|
|
61
|
-
baseAssetSymbol: '
|
|
60
|
+
symbol: '1MBONK-PERP',
|
|
61
|
+
baseAssetSymbol: '1MBONK',
|
|
62
62
|
marketIndex: 4,
|
|
63
63
|
oracle: new PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
|
|
64
64
|
launchTs: 1677068931000,
|
|
65
|
-
oracleSource: OracleSource.
|
|
65
|
+
oracleSource: OracleSource.PYTH_1M,
|
|
66
66
|
},
|
|
67
67
|
];
|
|
68
68
|
|
|
@@ -107,6 +107,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
107
107
|
launchTs: 1675802661000,
|
|
108
108
|
oracleSource: OracleSource.PYTH,
|
|
109
109
|
},
|
|
110
|
+
{
|
|
111
|
+
fullName: 'Bonk',
|
|
112
|
+
category: ['Meme'],
|
|
113
|
+
symbol: '1MBONK-PERP',
|
|
114
|
+
baseAssetSymbol: '1MBONK',
|
|
115
|
+
marketIndex: 4,
|
|
116
|
+
oracle: new PublicKey('8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN'),
|
|
117
|
+
launchTs: 1677690149000,
|
|
118
|
+
oracleSource: OracleSource.PYTH_1M,
|
|
119
|
+
},
|
|
110
120
|
];
|
|
111
121
|
|
|
112
122
|
export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
|
package/src/dlob/DLOB.ts
CHANGED
|
@@ -450,8 +450,9 @@ export class DLOB {
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
public getOrder(orderId: number, userAccount: PublicKey): Order | undefined {
|
|
453
|
+
const orderSignature = getOrderSignature(orderId, userAccount);
|
|
453
454
|
for (const nodeList of this.getNodeLists()) {
|
|
454
|
-
const node = nodeList.get(
|
|
455
|
+
const node = nodeList.get(orderSignature);
|
|
455
456
|
if (node) {
|
|
456
457
|
return node.order;
|
|
457
458
|
}
|
|
@@ -1383,18 +1384,29 @@ export class DLOB {
|
|
|
1383
1384
|
const bidSlot = bidNode.order.slot.add(
|
|
1384
1385
|
new BN(bidNode.order.auctionDuration)
|
|
1385
1386
|
);
|
|
1386
|
-
|
|
1387
|
+
|
|
1388
|
+
if (bidNode.order.postOnly && askNode.order.postOnly) {
|
|
1389
|
+
return undefined;
|
|
1390
|
+
} else if (bidNode.order.postOnly) {
|
|
1391
|
+
return {
|
|
1392
|
+
takerNode: askNode,
|
|
1393
|
+
makerNode: bidNode,
|
|
1394
|
+
};
|
|
1395
|
+
} else if (askNode.order.postOnly) {
|
|
1387
1396
|
return {
|
|
1388
1397
|
takerNode: bidNode,
|
|
1389
1398
|
makerNode: askNode,
|
|
1390
1399
|
};
|
|
1391
|
-
} else if (
|
|
1400
|
+
} else if (askSlot.lte(bidSlot)) {
|
|
1401
|
+
return {
|
|
1402
|
+
takerNode: bidNode,
|
|
1403
|
+
makerNode: askNode,
|
|
1404
|
+
};
|
|
1405
|
+
} else {
|
|
1392
1406
|
return {
|
|
1393
1407
|
takerNode: askNode,
|
|
1394
1408
|
makerNode: bidNode,
|
|
1395
1409
|
};
|
|
1396
|
-
} else {
|
|
1397
|
-
return undefined;
|
|
1398
1410
|
}
|
|
1399
1411
|
}
|
|
1400
1412
|
|
package/src/dlob/NodeList.ts
CHANGED
|
@@ -146,11 +146,8 @@ export class NodeList<NodeType extends keyof DLOBNodeMap>
|
|
|
146
146
|
return this.nodeMap.has(getOrderSignature(order.orderId, userAccount));
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
public get(
|
|
150
|
-
|
|
151
|
-
userAccount: PublicKey
|
|
152
|
-
): DLOBNodeMap[NodeType] | undefined {
|
|
153
|
-
return this.nodeMap.get(getOrderSignature(orderId, userAccount));
|
|
149
|
+
public get(orderSignature: string): DLOBNodeMap[NodeType] | undefined {
|
|
150
|
+
return this.nodeMap.get(orderSignature);
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
public print(): void {
|
package/src/driftClient.ts
CHANGED
|
@@ -2214,7 +2214,7 @@ export class DriftClient {
|
|
|
2214
2214
|
userAccountPublicKey: PublicKey,
|
|
2215
2215
|
user: UserAccount,
|
|
2216
2216
|
order?: Pick<Order, 'marketIndex' | 'orderId'>,
|
|
2217
|
-
makerInfo?: MakerInfo,
|
|
2217
|
+
makerInfo?: MakerInfo | MakerInfo[],
|
|
2218
2218
|
referrerInfo?: ReferrerInfo,
|
|
2219
2219
|
txParams?: TxParams
|
|
2220
2220
|
): Promise<TransactionSignature> {
|
|
@@ -2240,7 +2240,7 @@ export class DriftClient {
|
|
|
2240
2240
|
userAccountPublicKey: PublicKey,
|
|
2241
2241
|
userAccount: UserAccount,
|
|
2242
2242
|
order: Pick<Order, 'marketIndex' | 'orderId'>,
|
|
2243
|
-
makerInfo?: MakerInfo,
|
|
2243
|
+
makerInfo?: MakerInfo | MakerInfo[],
|
|
2244
2244
|
referrerInfo?: ReferrerInfo
|
|
2245
2245
|
): Promise<TransactionInstruction> {
|
|
2246
2246
|
const userStatsPublicKey = getUserStatsAccountPublicKey(
|
|
@@ -2257,45 +2257,54 @@ export class DriftClient {
|
|
|
2257
2257
|
(order) => order.orderId === userAccount.nextOrderId - 1
|
|
2258
2258
|
).marketIndex;
|
|
2259
2259
|
|
|
2260
|
+
makerInfo = Array.isArray(makerInfo)
|
|
2261
|
+
? makerInfo
|
|
2262
|
+
: makerInfo
|
|
2263
|
+
? [makerInfo]
|
|
2264
|
+
: [];
|
|
2265
|
+
|
|
2260
2266
|
const userAccounts = [userAccount];
|
|
2261
|
-
|
|
2262
|
-
userAccounts.push(
|
|
2267
|
+
for (const maker of makerInfo) {
|
|
2268
|
+
userAccounts.push(maker.makerUserAccount);
|
|
2263
2269
|
}
|
|
2264
2270
|
const remainingAccounts = this.getRemainingAccounts({
|
|
2265
2271
|
userAccounts,
|
|
2266
2272
|
writablePerpMarketIndexes: [marketIndex],
|
|
2267
2273
|
});
|
|
2268
2274
|
|
|
2269
|
-
|
|
2275
|
+
for (const maker of makerInfo) {
|
|
2270
2276
|
remainingAccounts.push({
|
|
2271
|
-
pubkey:
|
|
2277
|
+
pubkey: maker.maker,
|
|
2272
2278
|
isWritable: true,
|
|
2273
2279
|
isSigner: false,
|
|
2274
2280
|
});
|
|
2275
2281
|
remainingAccounts.push({
|
|
2276
|
-
pubkey:
|
|
2282
|
+
pubkey: maker.makerStats,
|
|
2277
2283
|
isWritable: true,
|
|
2278
2284
|
isSigner: false,
|
|
2279
2285
|
});
|
|
2280
2286
|
}
|
|
2281
2287
|
|
|
2282
2288
|
if (referrerInfo) {
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2289
|
+
const referrerIsMaker =
|
|
2290
|
+
makerInfo.find((maker) => maker.maker.equals(referrerInfo.referrer)) !==
|
|
2291
|
+
undefined;
|
|
2292
|
+
if (!referrerIsMaker) {
|
|
2293
|
+
remainingAccounts.push({
|
|
2294
|
+
pubkey: referrerInfo.referrer,
|
|
2295
|
+
isWritable: true,
|
|
2296
|
+
isSigner: false,
|
|
2297
|
+
});
|
|
2298
|
+
remainingAccounts.push({
|
|
2299
|
+
pubkey: referrerInfo.referrerStats,
|
|
2300
|
+
isWritable: true,
|
|
2301
|
+
isSigner: false,
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2293
2304
|
}
|
|
2294
2305
|
|
|
2295
2306
|
const orderId = order.orderId;
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
|
|
2307
|
+
return await this.program.instruction.fillPerpOrder(orderId, null, {
|
|
2299
2308
|
accounts: {
|
|
2300
2309
|
state: await this.getStatePublicKey(),
|
|
2301
2310
|
filler: fillerPublicKey,
|
|
@@ -14,10 +14,14 @@ export function getOracleClient(
|
|
|
14
14
|
return new PythClient(connection);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (isVariant(oracleSource, '
|
|
17
|
+
if (isVariant(oracleSource, 'pyth1K')) {
|
|
18
18
|
return new PythClient(connection, new BN(1000));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
if (isVariant(oracleSource, 'pyth1M')) {
|
|
22
|
+
return new PythClient(connection, new BN(1000000));
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
// if (isVariant(oracleSource, 'switchboard')) {
|
|
22
26
|
// return new SwitchboardClient(connection);
|
|
23
27
|
// }
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.20.0-beta.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -6207,6 +6207,7 @@
|
|
|
6207
6207
|
{
|
|
6208
6208
|
"name": "Match",
|
|
6209
6209
|
"fields": [
|
|
6210
|
+
"publicKey",
|
|
6210
6211
|
{
|
|
6211
6212
|
"defined": "usize"
|
|
6212
6213
|
}
|
|
@@ -6237,14 +6238,17 @@
|
|
|
6237
6238
|
{
|
|
6238
6239
|
"name": "Pyth"
|
|
6239
6240
|
},
|
|
6240
|
-
{
|
|
6241
|
-
"name": "Pyth1000"
|
|
6242
|
-
},
|
|
6243
6241
|
{
|
|
6244
6242
|
"name": "Switchboard"
|
|
6245
6243
|
},
|
|
6246
6244
|
{
|
|
6247
6245
|
"name": "QuoteAsset"
|
|
6246
|
+
},
|
|
6247
|
+
{
|
|
6248
|
+
"name": "Pyth1K"
|
|
6249
|
+
},
|
|
6250
|
+
{
|
|
6251
|
+
"name": "Pyth1M"
|
|
6248
6252
|
}
|
|
6249
6253
|
]
|
|
6250
6254
|
}
|
|
@@ -8601,6 +8605,56 @@
|
|
|
8601
8605
|
"code": 6227,
|
|
8602
8606
|
"name": "TierViolationLiquidatingPerpPnl",
|
|
8603
8607
|
"msg": "TierViolationLiquidatingPerpPnl"
|
|
8608
|
+
},
|
|
8609
|
+
{
|
|
8610
|
+
"code": 6228,
|
|
8611
|
+
"name": "CouldNotLoadUserData",
|
|
8612
|
+
"msg": "CouldNotLoadUserData"
|
|
8613
|
+
},
|
|
8614
|
+
{
|
|
8615
|
+
"code": 6229,
|
|
8616
|
+
"name": "UserWrongMutability",
|
|
8617
|
+
"msg": "UserWrongMutability"
|
|
8618
|
+
},
|
|
8619
|
+
{
|
|
8620
|
+
"code": 6230,
|
|
8621
|
+
"name": "InvalidUserAccount",
|
|
8622
|
+
"msg": "InvalidUserAccount"
|
|
8623
|
+
},
|
|
8624
|
+
{
|
|
8625
|
+
"code": 6231,
|
|
8626
|
+
"name": "CouldNotLoadUserStatsData",
|
|
8627
|
+
"msg": "CouldNotLoadUserData"
|
|
8628
|
+
},
|
|
8629
|
+
{
|
|
8630
|
+
"code": 6232,
|
|
8631
|
+
"name": "UserStatsWrongMutability",
|
|
8632
|
+
"msg": "UserWrongMutability"
|
|
8633
|
+
},
|
|
8634
|
+
{
|
|
8635
|
+
"code": 6233,
|
|
8636
|
+
"name": "InvalidUserStatsAccount",
|
|
8637
|
+
"msg": "InvalidUserAccount"
|
|
8638
|
+
},
|
|
8639
|
+
{
|
|
8640
|
+
"code": 6234,
|
|
8641
|
+
"name": "UserNotFound",
|
|
8642
|
+
"msg": "UserNotFound"
|
|
8643
|
+
},
|
|
8644
|
+
{
|
|
8645
|
+
"code": 6235,
|
|
8646
|
+
"name": "UnableToLoadUserAccount",
|
|
8647
|
+
"msg": "UnableToLoadUserAccount"
|
|
8648
|
+
},
|
|
8649
|
+
{
|
|
8650
|
+
"code": 6236,
|
|
8651
|
+
"name": "UserStatsNotFound",
|
|
8652
|
+
"msg": "UserStatsNotFound"
|
|
8653
|
+
},
|
|
8654
|
+
{
|
|
8655
|
+
"code": 6237,
|
|
8656
|
+
"name": "UnableToLoadUserStatsAccount",
|
|
8657
|
+
"msg": "UnableToLoadUserStatsAccount"
|
|
8604
8658
|
}
|
|
8605
8659
|
]
|
|
8606
8660
|
}
|
package/src/math/amm.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
MARGIN_PRECISION,
|
|
12
12
|
PRICE_DIV_PEG,
|
|
13
13
|
PERCENTAGE_PRECISION,
|
|
14
|
-
BASE_PRECISION,
|
|
15
14
|
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
|
|
16
15
|
TWO,
|
|
17
16
|
} from '../constants/numericConstants';
|
|
@@ -362,10 +361,12 @@ export function calculateInventoryScale(
|
|
|
362
361
|
maxSpread: number
|
|
363
362
|
): number {
|
|
364
363
|
if (baseAssetAmountWithAmm.eq(ZERO)) {
|
|
365
|
-
return
|
|
364
|
+
return 1;
|
|
366
365
|
}
|
|
367
366
|
|
|
368
|
-
const
|
|
367
|
+
const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = BID_ASK_SPREAD_PRECISION.mul(
|
|
368
|
+
new BN(10)
|
|
369
|
+
);
|
|
369
370
|
// inventory skew
|
|
370
371
|
const [openBids, openAsks] = calculateMarketOpenBidAsk(
|
|
371
372
|
baseAssetReserve,
|
|
@@ -373,34 +374,32 @@ export function calculateInventoryScale(
|
|
|
373
374
|
maxBaseAssetReserve
|
|
374
375
|
);
|
|
375
376
|
|
|
376
|
-
const minSideLiquidity = BN.
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
const minSideLiquidity = BN.min(openBids.abs(), openAsks.abs());
|
|
378
|
+
|
|
379
|
+
const inventoryScaleBN = BN.min(
|
|
380
|
+
baseAssetAmountWithAmm
|
|
381
|
+
.mul(PERCENTAGE_PRECISION)
|
|
382
|
+
.div(BN.max(minSideLiquidity, ONE))
|
|
383
|
+
.abs(),
|
|
384
|
+
PERCENTAGE_PRECISION
|
|
379
385
|
);
|
|
380
386
|
|
|
381
387
|
const inventoryScaleMaxBN = BN.max(
|
|
382
|
-
|
|
383
|
-
new BN(maxSpread
|
|
388
|
+
MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
|
|
389
|
+
new BN(maxSpread)
|
|
384
390
|
.mul(BID_ASK_SPREAD_PRECISION)
|
|
385
391
|
.div(new BN(Math.max(directionalSpread, 1)))
|
|
386
392
|
);
|
|
387
393
|
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const inventoryScale =
|
|
396
|
-
BN.min(inventoryScaleMaxBN, inventoryScaleBN).toNumber() /
|
|
397
|
-
BID_ASK_SPREAD_PRECISION.toNumber();
|
|
398
|
-
|
|
399
|
-
const inventoryScaleMax =
|
|
400
|
-
inventoryScaleMaxBN.toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
401
|
-
const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
|
|
394
|
+
const inventoryScaleCapped =
|
|
395
|
+
BN.min(
|
|
396
|
+
inventoryScaleMaxBN,
|
|
397
|
+
BID_ASK_SPREAD_PRECISION.add(
|
|
398
|
+
inventoryScaleMaxBN.mul(inventoryScaleBN).div(PERCENTAGE_PRECISION)
|
|
399
|
+
)
|
|
400
|
+
).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
|
|
402
401
|
|
|
403
|
-
return
|
|
402
|
+
return inventoryScaleCapped;
|
|
404
403
|
}
|
|
405
404
|
|
|
406
405
|
export function calculateEffectiveLeverage(
|
package/src/math/trade.ts
CHANGED
|
@@ -782,7 +782,7 @@ export function calculateEstimatedSpotEntryPrice(
|
|
|
782
782
|
cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
|
|
783
783
|
cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
|
|
784
784
|
|
|
785
|
-
worstPrice =
|
|
785
|
+
worstPrice = dlobLimitOrderPrice;
|
|
786
786
|
|
|
787
787
|
dlobLimitOrder = dlobLimitOrders.next().value;
|
|
788
788
|
} else {
|
|
@@ -9,26 +9,32 @@ export class SerumSubscriber {
|
|
|
9
9
|
connection: Connection;
|
|
10
10
|
programId: PublicKey;
|
|
11
11
|
marketAddress: PublicKey;
|
|
12
|
-
|
|
12
|
+
subscriptionType: 'polling' | 'websocket';
|
|
13
|
+
accountLoader: BulkAccountLoader | undefined;
|
|
13
14
|
market: Market;
|
|
14
15
|
|
|
15
16
|
subscribed: boolean;
|
|
16
17
|
|
|
17
18
|
asksAddress: PublicKey;
|
|
18
19
|
asks: Orderbook;
|
|
19
|
-
asksCallbackId: string;
|
|
20
|
+
asksCallbackId: string | number;
|
|
20
21
|
lastAsksSlot: number;
|
|
21
22
|
|
|
22
23
|
bidsAddress: PublicKey;
|
|
23
24
|
bids: Orderbook;
|
|
24
|
-
bidsCallbackId: string;
|
|
25
|
+
bidsCallbackId: string | number;
|
|
25
26
|
lastBidsSlot: number;
|
|
26
27
|
|
|
27
28
|
public constructor(config: SerumMarketSubscriberConfig) {
|
|
28
29
|
this.connection = config.connection;
|
|
29
30
|
this.programId = config.programId;
|
|
30
31
|
this.marketAddress = config.marketAddress;
|
|
31
|
-
|
|
32
|
+
if (config.accountSubscription.type === 'polling') {
|
|
33
|
+
this.subscriptionType = 'polling';
|
|
34
|
+
this.accountLoader = config.accountSubscription.accountLoader;
|
|
35
|
+
} else {
|
|
36
|
+
this.subscriptionType = 'websocket';
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
public async subscribe(): Promise<void> {
|
|
@@ -46,24 +52,44 @@ export class SerumSubscriber {
|
|
|
46
52
|
this.asksAddress = this.market.asksAddress;
|
|
47
53
|
this.asks = await this.market.loadAsks(this.connection);
|
|
48
54
|
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
if (this.subscriptionType === 'websocket') {
|
|
56
|
+
this.asksCallbackId = this.connection.onAccountChange(
|
|
57
|
+
this.asksAddress,
|
|
58
|
+
(accountInfo, ctx) => {
|
|
59
|
+
this.lastAsksSlot = ctx.slot;
|
|
60
|
+
this.asks = Orderbook.decode(this.market, accountInfo.data);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
this.asksCallbackId = await this.accountLoader.addAccount(
|
|
65
|
+
this.asksAddress,
|
|
66
|
+
(buffer, slot) => {
|
|
67
|
+
this.lastAsksSlot = slot;
|
|
68
|
+
this.asks = Orderbook.decode(this.market, buffer);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
56
72
|
|
|
57
73
|
this.bidsAddress = this.market.bidsAddress;
|
|
58
74
|
this.bids = await this.market.loadBids(this.connection);
|
|
59
75
|
|
|
60
|
-
this.
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
if (this.subscriptionType === 'websocket') {
|
|
77
|
+
this.bidsCallbackId = this.connection.onAccountChange(
|
|
78
|
+
this.bidsAddress,
|
|
79
|
+
(accountInfo, ctx) => {
|
|
80
|
+
this.lastBidsSlot = ctx.slot;
|
|
81
|
+
this.bids = Orderbook.decode(this.market, accountInfo.data);
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
this.bidsCallbackId = await this.accountLoader.addAccount(
|
|
86
|
+
this.bidsAddress,
|
|
87
|
+
(buffer, slot) => {
|
|
88
|
+
this.lastBidsSlot = slot;
|
|
89
|
+
this.bids = Orderbook.decode(this.market, buffer);
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
67
93
|
|
|
68
94
|
this.subscribed = true;
|
|
69
95
|
}
|
|
@@ -91,8 +117,24 @@ export class SerumSubscriber {
|
|
|
91
117
|
return;
|
|
92
118
|
}
|
|
93
119
|
|
|
94
|
-
|
|
95
|
-
|
|
120
|
+
// remove listeners
|
|
121
|
+
if (this.subscriptionType === 'websocket') {
|
|
122
|
+
await this.connection.removeAccountChangeListener(
|
|
123
|
+
this.asksCallbackId as number
|
|
124
|
+
);
|
|
125
|
+
await this.connection.removeAccountChangeListener(
|
|
126
|
+
this.bidsCallbackId as number
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
this.accountLoader.removeAccount(
|
|
130
|
+
this.asksAddress,
|
|
131
|
+
this.asksCallbackId as string
|
|
132
|
+
);
|
|
133
|
+
this.accountLoader.removeAccount(
|
|
134
|
+
this.bidsAddress,
|
|
135
|
+
this.bidsCallbackId as string
|
|
136
|
+
);
|
|
137
|
+
}
|
|
96
138
|
|
|
97
139
|
this.subscribed = false;
|
|
98
140
|
}
|
package/src/serum/types.ts
CHANGED
|
@@ -5,9 +5,13 @@ export type SerumMarketSubscriberConfig = {
|
|
|
5
5
|
connection: Connection;
|
|
6
6
|
programId: PublicKey;
|
|
7
7
|
marketAddress: PublicKey;
|
|
8
|
-
accountSubscription:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
accountSubscription:
|
|
9
|
+
| {
|
|
10
|
+
// enables use to add web sockets in the future
|
|
11
|
+
type: 'polling';
|
|
12
|
+
accountLoader: BulkAccountLoader;
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
type: 'websocket';
|
|
16
|
+
};
|
|
13
17
|
};
|
package/src/types.ts
CHANGED
|
@@ -76,7 +76,8 @@ export class DepositDirection {
|
|
|
76
76
|
|
|
77
77
|
export class OracleSource {
|
|
78
78
|
static readonly PYTH = { pyth: {} };
|
|
79
|
-
static readonly
|
|
79
|
+
static readonly PYTH_1K = { pyth1K: {} };
|
|
80
|
+
static readonly PYTH_1M = { pyth1M: {} };
|
|
80
81
|
// static readonly SWITCHBOARD = { switchboard: {} };
|
|
81
82
|
static readonly QUOTE_ASSET = { quoteAsset: {} };
|
|
82
83
|
}
|
|
@@ -901,7 +902,7 @@ export type MakerInfo = {
|
|
|
901
902
|
maker: PublicKey;
|
|
902
903
|
makerStats: PublicKey;
|
|
903
904
|
makerUserAccount: UserAccount;
|
|
904
|
-
order
|
|
905
|
+
order?: Order;
|
|
905
906
|
};
|
|
906
907
|
|
|
907
908
|
export type TakerInfo = {
|
package/tests/amm/test.ts
CHANGED
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
QUOTE_PRECISION,
|
|
7
7
|
calculateSpreadBN,
|
|
8
8
|
ZERO,
|
|
9
|
+
ONE,
|
|
9
10
|
calculateLiveOracleStd,
|
|
10
11
|
calculateLiveOracleTwap,
|
|
12
|
+
calculateInventoryScale,
|
|
11
13
|
} from '../../src';
|
|
12
14
|
import { mockPerpMarkets } from '../dlob/helpers';
|
|
13
15
|
|
|
@@ -36,6 +38,176 @@ class AMMSpreadTerms {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
describe('AMM Tests', () => {
|
|
41
|
+
it('Spread Maths', () => {
|
|
42
|
+
let iscale = calculateInventoryScale(
|
|
43
|
+
ZERO,
|
|
44
|
+
AMM_RESERVE_PRECISION,
|
|
45
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
46
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
47
|
+
250,
|
|
48
|
+
30000,
|
|
49
|
+
);
|
|
50
|
+
assert(iscale == 1);
|
|
51
|
+
|
|
52
|
+
iscale = calculateInventoryScale(
|
|
53
|
+
ONE,
|
|
54
|
+
AMM_RESERVE_PRECISION,
|
|
55
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
56
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
57
|
+
250,
|
|
58
|
+
30000,
|
|
59
|
+
);
|
|
60
|
+
assert(iscale == 1);
|
|
61
|
+
|
|
62
|
+
let baa = new BN(1000);
|
|
63
|
+
iscale = calculateInventoryScale(
|
|
64
|
+
baa,
|
|
65
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
66
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
67
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
68
|
+
250,
|
|
69
|
+
30000,
|
|
70
|
+
);
|
|
71
|
+
console.log(iscale);
|
|
72
|
+
assert(iscale == 1.00024);
|
|
73
|
+
|
|
74
|
+
baa = new BN(100000);
|
|
75
|
+
iscale = calculateInventoryScale(
|
|
76
|
+
baa,
|
|
77
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
78
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
79
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
80
|
+
250,
|
|
81
|
+
30000,
|
|
82
|
+
);
|
|
83
|
+
console.log(iscale);
|
|
84
|
+
assert(iscale == 1.024);
|
|
85
|
+
|
|
86
|
+
baa = new BN(1000000);
|
|
87
|
+
iscale = calculateInventoryScale(
|
|
88
|
+
baa,
|
|
89
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
90
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
91
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
92
|
+
250,
|
|
93
|
+
30000,
|
|
94
|
+
);
|
|
95
|
+
console.log(iscale);
|
|
96
|
+
assert(iscale == 1.24048);
|
|
97
|
+
|
|
98
|
+
baa = new BN(10000000); // 2%
|
|
99
|
+
iscale = calculateInventoryScale(
|
|
100
|
+
baa,
|
|
101
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
102
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
103
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
104
|
+
250,
|
|
105
|
+
30000,
|
|
106
|
+
);
|
|
107
|
+
console.log(iscale);
|
|
108
|
+
assert(iscale == 3.44896);
|
|
109
|
+
|
|
110
|
+
baa = new BN(50000000); // 10%
|
|
111
|
+
iscale = calculateInventoryScale(
|
|
112
|
+
baa,
|
|
113
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
114
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
115
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
116
|
+
250,
|
|
117
|
+
30000,
|
|
118
|
+
);
|
|
119
|
+
console.log(iscale);
|
|
120
|
+
assert(iscale == 14.33332);
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
baa = AMM_RESERVE_PRECISION.div(new BN(4)); // 50%
|
|
124
|
+
iscale = calculateInventoryScale(
|
|
125
|
+
baa,
|
|
126
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
127
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
128
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
129
|
+
250,
|
|
130
|
+
30000,
|
|
131
|
+
);
|
|
132
|
+
console.log(iscale);
|
|
133
|
+
assert(iscale == 120); //100%
|
|
134
|
+
|
|
135
|
+
baa = AMM_RESERVE_PRECISION.div(new BN(4)); // 50%
|
|
136
|
+
iscale = calculateInventoryScale(
|
|
137
|
+
baa,
|
|
138
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
139
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
140
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
141
|
+
250,
|
|
142
|
+
30000 * 2,
|
|
143
|
+
);
|
|
144
|
+
console.log(iscale);
|
|
145
|
+
assert(iscale == 120 * 2); //100%
|
|
146
|
+
|
|
147
|
+
baa = AMM_RESERVE_PRECISION.div(new BN(5)); // <50%
|
|
148
|
+
iscale = calculateInventoryScale(
|
|
149
|
+
baa,
|
|
150
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
151
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
152
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
153
|
+
250,
|
|
154
|
+
30000 * 2,
|
|
155
|
+
);
|
|
156
|
+
assert(iscale == 160.99984);
|
|
157
|
+
|
|
158
|
+
baa = new BN(855329058);
|
|
159
|
+
iscale = calculateInventoryScale(
|
|
160
|
+
baa,
|
|
161
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
162
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
163
|
+
AMM_RESERVE_PRECISION,
|
|
164
|
+
250,
|
|
165
|
+
30000,
|
|
166
|
+
); // >100%
|
|
167
|
+
assert(iscale == 120);
|
|
168
|
+
assert(250*iscale == 30000);
|
|
169
|
+
|
|
170
|
+
iscale = calculateInventoryScale(
|
|
171
|
+
baa,
|
|
172
|
+
AMM_RESERVE_PRECISION.add(baa), // ~85%
|
|
173
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
174
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
175
|
+
250,
|
|
176
|
+
30000,
|
|
177
|
+
);
|
|
178
|
+
assert(iscale == 120);
|
|
179
|
+
assert(250*iscale == 30000);
|
|
180
|
+
|
|
181
|
+
baa = new BN(-855329058); // ~85%
|
|
182
|
+
iscale = calculateInventoryScale(
|
|
183
|
+
baa,
|
|
184
|
+
AMM_RESERVE_PRECISION.add(baa),
|
|
185
|
+
AMM_RESERVE_PRECISION.div(new BN(2)),
|
|
186
|
+
AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
|
|
187
|
+
250,
|
|
188
|
+
30000,
|
|
189
|
+
);
|
|
190
|
+
assert(iscale == 120);
|
|
191
|
+
assert(250*iscale == 30000);
|
|
192
|
+
|
|
193
|
+
// 'bonk' scale
|
|
194
|
+
iscale = calculateInventoryScale(
|
|
195
|
+
new BN('30228000000000000'),
|
|
196
|
+
new BN('2496788386034912600'),
|
|
197
|
+
new BN('2443167585342470000'),
|
|
198
|
+
new BN('2545411471321696000'),
|
|
199
|
+
3500,
|
|
200
|
+
100000,
|
|
201
|
+
);
|
|
202
|
+
console.log(iscale);
|
|
203
|
+
console.log(3500*iscale/1e6);
|
|
204
|
+
assert(iscale == 18.762285);
|
|
205
|
+
assert(3500*iscale/1e6 == 0.06566799749999999); //6.5%
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
});
|
|
210
|
+
|
|
39
211
|
it('Various Spreads', () => {
|
|
40
212
|
const baseSpread: number = 0.025 * 1e6;
|
|
41
213
|
const lastOracleReservePriceSpreadPct: BN = ZERO;
|
|
@@ -150,9 +322,9 @@ describe('AMM Tests', () => {
|
|
|
150
322
|
|
|
151
323
|
console.log(terms2);
|
|
152
324
|
assert(terms2.effectiveLeverageCapped >= 1.0002);
|
|
153
|
-
assert(terms2.inventorySpreadScale ==
|
|
154
|
-
assert(terms2.longSpread ==
|
|
155
|
-
assert(terms2.shortSpread ==
|
|
325
|
+
assert(terms2.inventorySpreadScale == 1.73492);
|
|
326
|
+
assert(terms2.longSpread == 4262);
|
|
327
|
+
assert(terms2.shortSpread == 43238);
|
|
156
328
|
});
|
|
157
329
|
|
|
158
330
|
it('Corner Case Spreads', () => {
|
|
@@ -183,8 +355,8 @@ describe('AMM Tests', () => {
|
|
|
183
355
|
|
|
184
356
|
console.log(terms2);
|
|
185
357
|
assert(terms2.effectiveLeverageCapped <= 1.000001);
|
|
186
|
-
assert(terms2.inventorySpreadScale == 1.
|
|
187
|
-
assert(terms2.longSpread ==
|
|
358
|
+
assert(terms2.inventorySpreadScale == 1.013527);
|
|
359
|
+
assert(terms2.longSpread == 1146);
|
|
188
360
|
assert(terms2.shortSpread == 6686);
|
|
189
361
|
});
|
|
190
362
|
|
package/tests/dlob/test.ts
CHANGED
|
@@ -5207,7 +5207,8 @@ describe('DLOB Spot Tests', () => {
|
|
|
5207
5207
|
expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(3);
|
|
5208
5208
|
});
|
|
5209
5209
|
|
|
5210
|
-
|
|
5210
|
+
// add back if dlob checks limit order age again
|
|
5211
|
+
it.skip('Test limit orders skipping more recent post onlys', () => {
|
|
5211
5212
|
const vAsk = new BN(15);
|
|
5212
5213
|
const vBid = new BN(8);
|
|
5213
5214
|
|