@drift-labs/sdk 2.143.0-beta.0 → 2.143.0-beta.2
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/browser/accounts/grpcDriftClientAccountSubscriberV2.js +39 -7
- package/lib/browser/math/orders.d.ts +9 -0
- package/lib/browser/math/orders.js +44 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +39 -7
- package/lib/node/math/orders.d.ts +9 -0
- package/lib/node/math/orders.d.ts.map +1 -1
- package/lib/node/math/orders.js +44 -1
- package/package.json +1 -1
- package/scripts/single-grpc-client-test.ts +226 -0
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +43 -4
- package/src/math/orders.ts +70 -1
- /package/scripts/{client-test.ts → grpc-client-test-comparison.ts} +0 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.143.0-beta.
|
|
1
|
+
2.143.0-beta.2
|
|
@@ -106,9 +106,10 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
106
106
|
}
|
|
107
107
|
const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
|
|
108
108
|
o.publicKey.equals(oracleInfo.publicKey));
|
|
109
|
-
if (
|
|
110
|
-
|
|
109
|
+
if (exists) {
|
|
110
|
+
return true; // Already exists, don't add duplicate
|
|
111
111
|
}
|
|
112
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
112
113
|
(_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.addAccounts([oracleInfo.publicKey]);
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
@@ -412,10 +413,31 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
412
413
|
// Remove accounts in batches - perp markets
|
|
413
414
|
if (perpMarketPubkeysToRemove.length > 0) {
|
|
414
415
|
await this.perpMarketsSubscriber.removeAccounts(perpMarketPubkeysToRemove);
|
|
416
|
+
// Clean up the mapping for removed perp markets
|
|
417
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
418
|
+
const pubkeyString = pubkey.toBase58();
|
|
419
|
+
for (const [marketIndex, accountPubkey,] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
420
|
+
if (accountPubkey === pubkeyString) {
|
|
421
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
422
|
+
this.perpOracleMap.delete(marketIndex);
|
|
423
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
415
428
|
}
|
|
416
429
|
// Remove accounts in batches - oracles
|
|
417
430
|
if (oraclePubkeysToRemove.length > 0) {
|
|
418
431
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
432
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
433
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
434
|
+
// Find the oracle source by checking oracleInfos
|
|
435
|
+
const oracleInfo = this.oracleInfos.find((info) => info.publicKey.equals(pubkey));
|
|
436
|
+
if (oracleInfo) {
|
|
437
|
+
const oracleId = (0, oracleId_1.getOracleId)(pubkey, oracleInfo.source);
|
|
438
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
removeInitialData() {
|
|
@@ -431,14 +453,24 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
455
|
async unsubscribe() {
|
|
434
|
-
var _a, _c;
|
|
435
|
-
if (this.isSubscribed) {
|
|
456
|
+
var _a, _c, _d;
|
|
457
|
+
if (!this.isSubscribed) {
|
|
436
458
|
return;
|
|
437
459
|
}
|
|
438
|
-
|
|
460
|
+
this.isSubscribed = false;
|
|
461
|
+
this.isSubscribing = false;
|
|
462
|
+
await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
439
463
|
await this.unsubscribeFromOracles();
|
|
440
|
-
await ((
|
|
441
|
-
await ((
|
|
464
|
+
await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
|
|
465
|
+
await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.unsubscribe());
|
|
466
|
+
// Clean up all maps to prevent memory leaks
|
|
467
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
468
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
469
|
+
this.oracleIdToOracleDataMap.clear();
|
|
470
|
+
this.perpOracleMap.clear();
|
|
471
|
+
this.perpOracleStringMap.clear();
|
|
472
|
+
this.spotOracleMap.clear();
|
|
473
|
+
this.spotOracleStringMap.clear();
|
|
442
474
|
}
|
|
443
475
|
}
|
|
444
476
|
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
|
|
@@ -25,3 +25,12 @@ export declare function isTakingOrder(order: Order, slot: number): boolean;
|
|
|
25
25
|
export declare function isSignedMsgOrder(order: Order): boolean;
|
|
26
26
|
export declare function hasBuilder(order: Order): boolean;
|
|
27
27
|
export declare function calculateOrderBaseAssetAmount(order: Order, existingBaseAssetAmount: BN): BN;
|
|
28
|
+
/**
|
|
29
|
+
* Invert the size-premium liability weight: given a target margin ratio (liability weight),
|
|
30
|
+
* return the max `size` (AMM_RESERVE_PRECISION units) that still yields <= target.
|
|
31
|
+
*
|
|
32
|
+
* Returns:
|
|
33
|
+
* - BN size (>=0) if bounded
|
|
34
|
+
* - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
|
|
35
|
+
*/
|
|
36
|
+
export declare function maxSizeForTargetLiabilityWeightBN(target: BN, imfFactor: BN, liabilityWeight: BN): BN | null;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateOrderBaseAssetAmount = exports.hasBuilder = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
|
|
3
|
+
exports.maxSizeForTargetLiabilityWeightBN = exports.calculateOrderBaseAssetAmount = exports.hasBuilder = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
6
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
7
|
const auction_1 = require("./auction");
|
|
8
8
|
const amm_1 = require("./amm");
|
|
9
|
+
const margin_1 = require("./margin");
|
|
9
10
|
function isOrderRiskIncreasing(user, order) {
|
|
10
11
|
if (!(0, types_1.isVariant)(order.status, 'open')) {
|
|
11
12
|
return false;
|
|
@@ -257,3 +258,45 @@ function calculateOrderBaseAssetAmount(order, existingBaseAssetAmount) {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
exports.calculateOrderBaseAssetAmount = calculateOrderBaseAssetAmount;
|
|
261
|
+
// ---------- inverse ----------
|
|
262
|
+
/**
|
|
263
|
+
* Invert the size-premium liability weight: given a target margin ratio (liability weight),
|
|
264
|
+
* return the max `size` (AMM_RESERVE_PRECISION units) that still yields <= target.
|
|
265
|
+
*
|
|
266
|
+
* Returns:
|
|
267
|
+
* - BN size (>=0) if bounded
|
|
268
|
+
* - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
|
|
269
|
+
*/
|
|
270
|
+
function maxSizeForTargetLiabilityWeightBN(target, imfFactor, liabilityWeight) {
|
|
271
|
+
if (target.lt(liabilityWeight))
|
|
272
|
+
return null;
|
|
273
|
+
if (imfFactor.isZero())
|
|
274
|
+
return null;
|
|
275
|
+
const base = liabilityWeight.muln(4).divn(5);
|
|
276
|
+
const denom = new anchor_1.BN(100000)
|
|
277
|
+
.mul(numericConstants_1.SPOT_MARKET_IMF_PRECISION)
|
|
278
|
+
.div(numericConstants_1.MARGIN_PRECISION);
|
|
279
|
+
if (denom.isZero())
|
|
280
|
+
throw new Error('denom=0: bad precision/spotImfPrecision');
|
|
281
|
+
const allowedInc = target.gt(base) ? target.sub(base) : numericConstants_1.ZERO;
|
|
282
|
+
const maxSqrt = allowedInc.mul(denom).div(imfFactor);
|
|
283
|
+
if (maxSqrt.lte(numericConstants_1.ZERO)) {
|
|
284
|
+
const fitsZero = (0, margin_1.calculateSizePremiumLiabilityWeight)(numericConstants_1.ZERO, imfFactor, liabilityWeight, numericConstants_1.MARGIN_PRECISION).lte(target);
|
|
285
|
+
return fitsZero ? numericConstants_1.ZERO : null;
|
|
286
|
+
}
|
|
287
|
+
let hi = maxSqrt.mul(maxSqrt).sub(numericConstants_1.ONE).divn(10);
|
|
288
|
+
if (hi.isNeg())
|
|
289
|
+
hi = numericConstants_1.ZERO;
|
|
290
|
+
let lo = numericConstants_1.ZERO;
|
|
291
|
+
while (lo.lt(hi)) {
|
|
292
|
+
const mid = lo.add(hi).add(numericConstants_1.ONE).divn(2); // upper mid to prevent infinite loop
|
|
293
|
+
if ((0, margin_1.calculateSizePremiumLiabilityWeight)(mid, imfFactor, liabilityWeight, numericConstants_1.MARGIN_PRECISION).lte(target)) {
|
|
294
|
+
lo = mid;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
hi = mid.sub(numericConstants_1.ONE);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return lo;
|
|
301
|
+
}
|
|
302
|
+
exports.maxSizeForTargetLiabilityWeightBN = maxSizeForTargetLiabilityWeightBN;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grpcDriftClientAccountSubscriberV2.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcDriftClientAccountSubscriberV2.ts"],"names":[],"mappings":";AAAA,OAAO,kBAAkB,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAS5C,OAAO,EACN,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,wBAAwB,EACxB,4BAA4B,EAE5B,WAAW,EACX,SAAS,EACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAK9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,qBAAa,kCACZ,YAAW,4BAA4B;IAEvC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,qBAAqB,CAAC,CAAgD;IAC9E,OAAO,CAAC,qBAAqB,CAAC,CAAgD;IAC9E,OAAO,CAAC,qBAAqB,CAAC,CAG5B;IACF,OAAO,CAAC,iCAAiC,CAA6B;IACtE,OAAO,CAAC,iCAAiC,CAA6B;IACtE,OAAO,CAAC,qBAAqB,CAAwB;IAE9C,YAAY,EAAE,kBAAkB,CACtC,YAAY,EACZ,wBAAwB,CACxB,CAAC;IACK,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,8BAA8B,EAAE,OAAO,CAAC;IACxC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,4BAA4B,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC7D,4BAA4B,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC7D,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,aAAa,yBAAgC;IAC7C,mBAAmB,sBAA6B;IAChD,aAAa,yBAAgC;IAC7C,mBAAmB,sBAA6B;IACvD,OAAO,CAAC,uBAAuB,CAG3B;IACG,sBAAsB,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAChE,iBAAiB,oBAA2B;IAC5C,OAAO,CAAC,SAAS,CAAC,CAAY;IAE9B,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,SAAS,CAAC,2BAA2B,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;gBAG7D,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,EAAE,EAC3B,iBAAiB,EAAE,MAAM,EAAE,EAC3B,WAAW,EAAE,UAAU,EAAE,EACzB,8BAA8B,EAAE,OAAO,EACvC,qBAAqB,EAAE,qBAAqB,EAC5C,SAAS,CAAC,EAAE,SAAS;IAsBtB,MAAM,aAAc,SAAS,CAAC,EAAE,QAAQ,MAAM,KAAG,CAAC,EAAE,EAAE,CAKpD;IAEI,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA4F/B,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOrD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"grpcDriftClientAccountSubscriberV2.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcDriftClientAccountSubscriberV2.ts"],"names":[],"mappings":"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"}
|
|
@@ -106,9 +106,10 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
106
106
|
}
|
|
107
107
|
const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
|
|
108
108
|
o.publicKey.equals(oracleInfo.publicKey));
|
|
109
|
-
if (
|
|
110
|
-
|
|
109
|
+
if (exists) {
|
|
110
|
+
return true; // Already exists, don't add duplicate
|
|
111
111
|
}
|
|
112
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
112
113
|
(_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.addAccounts([oracleInfo.publicKey]);
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
@@ -412,10 +413,31 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
412
413
|
// Remove accounts in batches - perp markets
|
|
413
414
|
if (perpMarketPubkeysToRemove.length > 0) {
|
|
414
415
|
await this.perpMarketsSubscriber.removeAccounts(perpMarketPubkeysToRemove);
|
|
416
|
+
// Clean up the mapping for removed perp markets
|
|
417
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
418
|
+
const pubkeyString = pubkey.toBase58();
|
|
419
|
+
for (const [marketIndex, accountPubkey,] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
420
|
+
if (accountPubkey === pubkeyString) {
|
|
421
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
422
|
+
this.perpOracleMap.delete(marketIndex);
|
|
423
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
415
428
|
}
|
|
416
429
|
// Remove accounts in batches - oracles
|
|
417
430
|
if (oraclePubkeysToRemove.length > 0) {
|
|
418
431
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
432
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
433
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
434
|
+
// Find the oracle source by checking oracleInfos
|
|
435
|
+
const oracleInfo = this.oracleInfos.find((info) => info.publicKey.equals(pubkey));
|
|
436
|
+
if (oracleInfo) {
|
|
437
|
+
const oracleId = (0, oracleId_1.getOracleId)(pubkey, oracleInfo.source);
|
|
438
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
removeInitialData() {
|
|
@@ -431,14 +453,24 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
455
|
async unsubscribe() {
|
|
434
|
-
var _a, _c;
|
|
435
|
-
if (this.isSubscribed) {
|
|
456
|
+
var _a, _c, _d;
|
|
457
|
+
if (!this.isSubscribed) {
|
|
436
458
|
return;
|
|
437
459
|
}
|
|
438
|
-
|
|
460
|
+
this.isSubscribed = false;
|
|
461
|
+
this.isSubscribing = false;
|
|
462
|
+
await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
439
463
|
await this.unsubscribeFromOracles();
|
|
440
|
-
await ((
|
|
441
|
-
await ((
|
|
464
|
+
await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
|
|
465
|
+
await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.unsubscribe());
|
|
466
|
+
// Clean up all maps to prevent memory leaks
|
|
467
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
468
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
469
|
+
this.oracleIdToOracleDataMap.clear();
|
|
470
|
+
this.perpOracleMap.clear();
|
|
471
|
+
this.perpOracleStringMap.clear();
|
|
472
|
+
this.spotOracleMap.clear();
|
|
473
|
+
this.spotOracleStringMap.clear();
|
|
442
474
|
}
|
|
443
475
|
}
|
|
444
476
|
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
|
|
@@ -25,4 +25,13 @@ export declare function isTakingOrder(order: Order, slot: number): boolean;
|
|
|
25
25
|
export declare function isSignedMsgOrder(order: Order): boolean;
|
|
26
26
|
export declare function hasBuilder(order: Order): boolean;
|
|
27
27
|
export declare function calculateOrderBaseAssetAmount(order: Order, existingBaseAssetAmount: BN): BN;
|
|
28
|
+
/**
|
|
29
|
+
* Invert the size-premium liability weight: given a target margin ratio (liability weight),
|
|
30
|
+
* return the max `size` (AMM_RESERVE_PRECISION units) that still yields <= target.
|
|
31
|
+
*
|
|
32
|
+
* Returns:
|
|
33
|
+
* - BN size (>=0) if bounded
|
|
34
|
+
* - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
|
|
35
|
+
*/
|
|
36
|
+
export declare function maxSizeForTargetLiabilityWeightBN(target: BN, imfFactor: BN, liabilityWeight: BN): BN | null;
|
|
28
37
|
//# sourceMappingURL=orders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../src/math/orders.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAGN,iBAAiB,EACjB,GAAG,EACH,KAAK,EACL,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../src/math/orders.ts"],"names":[],"mappings":"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"}
|
package/lib/node/math/orders.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateOrderBaseAssetAmount = exports.hasBuilder = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
|
|
3
|
+
exports.maxSizeForTargetLiabilityWeightBN = exports.calculateOrderBaseAssetAmount = exports.hasBuilder = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
6
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
7
|
const auction_1 = require("./auction");
|
|
8
8
|
const amm_1 = require("./amm");
|
|
9
|
+
const margin_1 = require("./margin");
|
|
9
10
|
function isOrderRiskIncreasing(user, order) {
|
|
10
11
|
if (!(0, types_1.isVariant)(order.status, 'open')) {
|
|
11
12
|
return false;
|
|
@@ -257,3 +258,45 @@ function calculateOrderBaseAssetAmount(order, existingBaseAssetAmount) {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
exports.calculateOrderBaseAssetAmount = calculateOrderBaseAssetAmount;
|
|
261
|
+
// ---------- inverse ----------
|
|
262
|
+
/**
|
|
263
|
+
* Invert the size-premium liability weight: given a target margin ratio (liability weight),
|
|
264
|
+
* return the max `size` (AMM_RESERVE_PRECISION units) that still yields <= target.
|
|
265
|
+
*
|
|
266
|
+
* Returns:
|
|
267
|
+
* - BN size (>=0) if bounded
|
|
268
|
+
* - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
|
|
269
|
+
*/
|
|
270
|
+
function maxSizeForTargetLiabilityWeightBN(target, imfFactor, liabilityWeight) {
|
|
271
|
+
if (target.lt(liabilityWeight))
|
|
272
|
+
return null;
|
|
273
|
+
if (imfFactor.isZero())
|
|
274
|
+
return null;
|
|
275
|
+
const base = liabilityWeight.muln(4).divn(5);
|
|
276
|
+
const denom = new anchor_1.BN(100000)
|
|
277
|
+
.mul(numericConstants_1.SPOT_MARKET_IMF_PRECISION)
|
|
278
|
+
.div(numericConstants_1.MARGIN_PRECISION);
|
|
279
|
+
if (denom.isZero())
|
|
280
|
+
throw new Error('denom=0: bad precision/spotImfPrecision');
|
|
281
|
+
const allowedInc = target.gt(base) ? target.sub(base) : numericConstants_1.ZERO;
|
|
282
|
+
const maxSqrt = allowedInc.mul(denom).div(imfFactor);
|
|
283
|
+
if (maxSqrt.lte(numericConstants_1.ZERO)) {
|
|
284
|
+
const fitsZero = (0, margin_1.calculateSizePremiumLiabilityWeight)(numericConstants_1.ZERO, imfFactor, liabilityWeight, numericConstants_1.MARGIN_PRECISION).lte(target);
|
|
285
|
+
return fitsZero ? numericConstants_1.ZERO : null;
|
|
286
|
+
}
|
|
287
|
+
let hi = maxSqrt.mul(maxSqrt).sub(numericConstants_1.ONE).divn(10);
|
|
288
|
+
if (hi.isNeg())
|
|
289
|
+
hi = numericConstants_1.ZERO;
|
|
290
|
+
let lo = numericConstants_1.ZERO;
|
|
291
|
+
while (lo.lt(hi)) {
|
|
292
|
+
const mid = lo.add(hi).add(numericConstants_1.ONE).divn(2); // upper mid to prevent infinite loop
|
|
293
|
+
if ((0, margin_1.calculateSizePremiumLiabilityWeight)(mid, imfFactor, liabilityWeight, numericConstants_1.MARGIN_PRECISION).lte(target)) {
|
|
294
|
+
lo = mid;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
hi = mid.sub(numericConstants_1.ONE);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return lo;
|
|
301
|
+
}
|
|
302
|
+
exports.maxSizeForTargetLiabilityWeightBN = maxSizeForTargetLiabilityWeightBN;
|
package/package.json
CHANGED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { DriftClient } from '../src/driftClient';
|
|
2
|
+
import { grpcDriftClientAccountSubscriberV2 } from '../src/accounts/grpcDriftClientAccountSubscriberV2';
|
|
3
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { DriftClientConfig } from '../src/driftClientConfig';
|
|
5
|
+
import {
|
|
6
|
+
DRIFT_PROGRAM_ID,
|
|
7
|
+
PerpMarketAccount,
|
|
8
|
+
SpotMarketAccount,
|
|
9
|
+
Wallet,
|
|
10
|
+
OracleInfo,
|
|
11
|
+
decodeName,
|
|
12
|
+
} from '../src';
|
|
13
|
+
import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
|
|
14
|
+
import dotenv from 'dotenv';
|
|
15
|
+
import {
|
|
16
|
+
AnchorProvider,
|
|
17
|
+
Idl,
|
|
18
|
+
Program,
|
|
19
|
+
ProgramAccount,
|
|
20
|
+
} from '@coral-xyz/anchor';
|
|
21
|
+
import driftIDL from '../src/idl/drift.json';
|
|
22
|
+
|
|
23
|
+
const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
|
|
24
|
+
const TOKEN = process.env.TOKEN;
|
|
25
|
+
const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
|
|
26
|
+
|
|
27
|
+
async function initializeSingleGrpcClient() {
|
|
28
|
+
console.log('🚀 Initializing single gRPC Drift Client...');
|
|
29
|
+
|
|
30
|
+
const connection = new Connection(RPC_ENDPOINT);
|
|
31
|
+
const wallet = new Wallet(new Keypair());
|
|
32
|
+
dotenv.config({ path: '../' });
|
|
33
|
+
|
|
34
|
+
const programId = new PublicKey(DRIFT_PROGRAM_ID);
|
|
35
|
+
const provider = new AnchorProvider(
|
|
36
|
+
connection,
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
wallet,
|
|
39
|
+
{
|
|
40
|
+
commitment: 'processed',
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const program = new Program(driftIDL as Idl, programId, provider);
|
|
45
|
+
|
|
46
|
+
// Get perp market accounts
|
|
47
|
+
const allPerpMarketProgramAccounts =
|
|
48
|
+
(await program.account.perpMarket.all()) as ProgramAccount<PerpMarketAccount>[];
|
|
49
|
+
const perpMarketProgramAccounts = allPerpMarketProgramAccounts.filter((val) =>
|
|
50
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].includes(
|
|
51
|
+
val.account.marketIndex
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
const perpMarketIndexes = perpMarketProgramAccounts.map(
|
|
55
|
+
(val) => val.account.marketIndex
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Get spot market accounts
|
|
59
|
+
const allSpotMarketProgramAccounts =
|
|
60
|
+
(await program.account.spotMarket.all()) as ProgramAccount<SpotMarketAccount>[];
|
|
61
|
+
const spotMarketProgramAccounts = allSpotMarketProgramAccounts.filter((val) =>
|
|
62
|
+
[0, 1, 2, 3, 4, 5].includes(val.account.marketIndex)
|
|
63
|
+
);
|
|
64
|
+
const spotMarketIndexes = spotMarketProgramAccounts.map(
|
|
65
|
+
(val) => val.account.marketIndex
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Get oracle infos
|
|
69
|
+
const seen = new Set<string>();
|
|
70
|
+
const oracleInfos: OracleInfo[] = [];
|
|
71
|
+
for (const acct of perpMarketProgramAccounts) {
|
|
72
|
+
const key = `${acct.account.amm.oracle.toBase58()}-${
|
|
73
|
+
Object.keys(acct.account.amm.oracleSource)[0]
|
|
74
|
+
}`;
|
|
75
|
+
if (!seen.has(key)) {
|
|
76
|
+
seen.add(key);
|
|
77
|
+
oracleInfos.push({
|
|
78
|
+
publicKey: acct.account.amm.oracle,
|
|
79
|
+
source: acct.account.amm.oracleSource,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const acct of spotMarketProgramAccounts) {
|
|
84
|
+
const key = `${acct.account.oracle.toBase58()}-${
|
|
85
|
+
Object.keys(acct.account.oracleSource)[0]
|
|
86
|
+
}`;
|
|
87
|
+
if (!seen.has(key)) {
|
|
88
|
+
seen.add(key);
|
|
89
|
+
oracleInfos.push({
|
|
90
|
+
publicKey: acct.account.oracle,
|
|
91
|
+
source: acct.account.oracleSource,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`📊 Markets: ${perpMarketIndexes.length} perp, ${spotMarketIndexes.length} spot`);
|
|
97
|
+
console.log(`🔮 Oracles: ${oracleInfos.length}`);
|
|
98
|
+
|
|
99
|
+
const baseAccountSubscription = {
|
|
100
|
+
type: 'grpc' as const,
|
|
101
|
+
grpcConfigs: {
|
|
102
|
+
endpoint: GRPC_ENDPOINT,
|
|
103
|
+
token: TOKEN,
|
|
104
|
+
commitmentLevel: CommitmentLevel.PROCESSED,
|
|
105
|
+
channelOptions: {
|
|
106
|
+
'grpc.keepalive_time_ms': 10_000,
|
|
107
|
+
'grpc.keepalive_timeout_ms': 1_000,
|
|
108
|
+
'grpc.keepalive_permit_without_calls': 1,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const config: DriftClientConfig = {
|
|
114
|
+
connection,
|
|
115
|
+
wallet,
|
|
116
|
+
programID: new PublicKey(DRIFT_PROGRAM_ID),
|
|
117
|
+
accountSubscription: {
|
|
118
|
+
...baseAccountSubscription,
|
|
119
|
+
driftClientAccountSubscriber: grpcDriftClientAccountSubscriberV2,
|
|
120
|
+
},
|
|
121
|
+
perpMarketIndexes,
|
|
122
|
+
spotMarketIndexes,
|
|
123
|
+
oracleInfos,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const client = new DriftClient(config);
|
|
127
|
+
|
|
128
|
+
// Set up event listeners
|
|
129
|
+
const eventCounts = {
|
|
130
|
+
stateAccountUpdate: 0,
|
|
131
|
+
perpMarketAccountUpdate: 0,
|
|
132
|
+
spotMarketAccountUpdate: 0,
|
|
133
|
+
oraclePriceUpdate: 0,
|
|
134
|
+
update: 0,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
console.log('🎧 Setting up event listeners...');
|
|
138
|
+
|
|
139
|
+
client.eventEmitter.on('stateAccountUpdate', (_data) => {
|
|
140
|
+
eventCounts.stateAccountUpdate++;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
client.eventEmitter.on('perpMarketAccountUpdate', (_data) => {
|
|
144
|
+
eventCounts.perpMarketAccountUpdate++;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
client.eventEmitter.on('spotMarketAccountUpdate', (_data) => {
|
|
148
|
+
eventCounts.spotMarketAccountUpdate++;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
client.eventEmitter.on('oraclePriceUpdate', (_publicKey, _source, _data) => {
|
|
152
|
+
eventCounts.oraclePriceUpdate++;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
client.accountSubscriber.eventEmitter.on('update', () => {
|
|
156
|
+
eventCounts.update++;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Subscribe
|
|
160
|
+
console.log('🔗 Subscribing to accounts...');
|
|
161
|
+
await client.subscribe();
|
|
162
|
+
|
|
163
|
+
console.log('✅ Client subscribed successfully!');
|
|
164
|
+
console.log('🚀 Starting high-load testing (50 reads/sec per perp market)...');
|
|
165
|
+
|
|
166
|
+
// High-frequency load testing - 50 reads per second per perp market
|
|
167
|
+
const loadTestInterval = setInterval(async () => {
|
|
168
|
+
try {
|
|
169
|
+
// Test getPerpMarketAccount for each perp market (50 times per second per market)
|
|
170
|
+
for (const marketIndex of perpMarketIndexes) {
|
|
171
|
+
const perpMarketAccount = client.getPerpMarketAccount(marketIndex);
|
|
172
|
+
console.log("perpMarketAccount name: ", decodeName(perpMarketAccount.name));
|
|
173
|
+
console.log("perpMarketAccount data: ", JSON.stringify({
|
|
174
|
+
marketIndex: perpMarketAccount.marketIndex,
|
|
175
|
+
name: decodeName(perpMarketAccount.name),
|
|
176
|
+
baseAssetReserve: perpMarketAccount.amm.baseAssetReserve.toString(),
|
|
177
|
+
quoteAssetReserve: perpMarketAccount.amm.quoteAssetReserve.toString()
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Test getMMOracleDataForPerpMarket for each perp market (50 times per second per market)
|
|
182
|
+
for (const marketIndex of perpMarketIndexes) {
|
|
183
|
+
try {
|
|
184
|
+
const oracleData = client.getMMOracleDataForPerpMarket(marketIndex);
|
|
185
|
+
console.log("oracleData price: ", oracleData.price.toString());
|
|
186
|
+
console.log("oracleData: ", JSON.stringify({
|
|
187
|
+
price: oracleData.price.toString(),
|
|
188
|
+
confidence: oracleData.confidence?.toString(),
|
|
189
|
+
slot: oracleData.slot?.toString()
|
|
190
|
+
}));
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// Ignore errors for load testing
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error('Load test error:', error);
|
|
197
|
+
}
|
|
198
|
+
}, 20); // 50 times per second = 1000ms / 50 = 20ms interval
|
|
199
|
+
|
|
200
|
+
// Log periodic stats
|
|
201
|
+
const statsInterval = setInterval(() => {
|
|
202
|
+
console.log('\n📈 Event Counts:', eventCounts);
|
|
203
|
+
console.log(`⏱️ Client subscribed: ${client.isSubscribed}`);
|
|
204
|
+
console.log(`🔗 Account subscriber subscribed: ${client.accountSubscriber.isSubscribed}`);
|
|
205
|
+
console.log(`🔥 Load: ${perpMarketIndexes.length * 50 * 2} reads/sec (${perpMarketIndexes.length} markets × 50 getPerpMarketAccount + 50 getMMOracleDataForPerpMarket)`);
|
|
206
|
+
}, 5000);
|
|
207
|
+
|
|
208
|
+
// Handle shutdown signals - just exit without cleanup since they never unsubscribe
|
|
209
|
+
process.on('SIGINT', () => {
|
|
210
|
+
console.log('\n🛑 Shutting down...');
|
|
211
|
+
clearInterval(loadTestInterval);
|
|
212
|
+
clearInterval(statsInterval);
|
|
213
|
+
process.exit(0);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
process.on('SIGTERM', () => {
|
|
217
|
+
console.log('\n🛑 Shutting down...');
|
|
218
|
+
clearInterval(loadTestInterval);
|
|
219
|
+
clearInterval(statsInterval);
|
|
220
|
+
process.exit(0);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return client;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
initializeSingleGrpcClient().catch(console.error);
|
|
@@ -227,10 +227,11 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
227
227
|
o.source === oracleInfo.source &&
|
|
228
228
|
o.publicKey.equals(oracleInfo.publicKey)
|
|
229
229
|
);
|
|
230
|
-
if (
|
|
231
|
-
|
|
230
|
+
if (exists) {
|
|
231
|
+
return true; // Already exists, don't add duplicate
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
234
235
|
this.oracleMultiSubscriber?.addAccounts([oracleInfo.publicKey]);
|
|
235
236
|
|
|
236
237
|
return true;
|
|
@@ -708,11 +709,37 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
708
709
|
await this.perpMarketsSubscriber.removeAccounts(
|
|
709
710
|
perpMarketPubkeysToRemove
|
|
710
711
|
);
|
|
712
|
+
// Clean up the mapping for removed perp markets
|
|
713
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
714
|
+
const pubkeyString = pubkey.toBase58();
|
|
715
|
+
for (const [
|
|
716
|
+
marketIndex,
|
|
717
|
+
accountPubkey,
|
|
718
|
+
] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
719
|
+
if (accountPubkey === pubkeyString) {
|
|
720
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
721
|
+
this.perpOracleMap.delete(marketIndex);
|
|
722
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
711
727
|
}
|
|
712
728
|
|
|
713
729
|
// Remove accounts in batches - oracles
|
|
714
730
|
if (oraclePubkeysToRemove.length > 0) {
|
|
715
731
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
732
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
733
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
734
|
+
// Find the oracle source by checking oracleInfos
|
|
735
|
+
const oracleInfo = this.oracleInfos.find((info) =>
|
|
736
|
+
info.publicKey.equals(pubkey)
|
|
737
|
+
);
|
|
738
|
+
if (oracleInfo) {
|
|
739
|
+
const oracleId = getOracleId(pubkey, oracleInfo.source);
|
|
740
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
716
743
|
}
|
|
717
744
|
}
|
|
718
745
|
|
|
@@ -731,13 +758,25 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
731
758
|
}
|
|
732
759
|
|
|
733
760
|
async unsubscribe(): Promise<void> {
|
|
734
|
-
if (this.isSubscribed) {
|
|
761
|
+
if (!this.isSubscribed) {
|
|
735
762
|
return;
|
|
736
763
|
}
|
|
737
764
|
|
|
738
|
-
|
|
765
|
+
this.isSubscribed = false;
|
|
766
|
+
this.isSubscribing = false;
|
|
767
|
+
|
|
768
|
+
await this.stateAccountSubscriber?.unsubscribe();
|
|
739
769
|
await this.unsubscribeFromOracles();
|
|
740
770
|
await this.perpMarketsSubscriber?.unsubscribe();
|
|
741
771
|
await this.spotMarketsSubscriber?.unsubscribe();
|
|
772
|
+
|
|
773
|
+
// Clean up all maps to prevent memory leaks
|
|
774
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
775
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
776
|
+
this.oracleIdToOracleDataMap.clear();
|
|
777
|
+
this.perpOracleMap.clear();
|
|
778
|
+
this.perpOracleStringMap.clear();
|
|
779
|
+
this.spotOracleMap.clear();
|
|
780
|
+
this.spotOracleStringMap.clear();
|
|
742
781
|
}
|
|
743
782
|
}
|
package/src/math/orders.ts
CHANGED
|
@@ -9,7 +9,13 @@ import {
|
|
|
9
9
|
ProtectedMakerParams,
|
|
10
10
|
MarketTypeStr,
|
|
11
11
|
} from '../types';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ZERO,
|
|
14
|
+
TWO,
|
|
15
|
+
ONE,
|
|
16
|
+
SPOT_MARKET_IMF_PRECISION,
|
|
17
|
+
MARGIN_PRECISION,
|
|
18
|
+
} from '../constants/numericConstants';
|
|
13
19
|
import { BN } from '@coral-xyz/anchor';
|
|
14
20
|
import { MMOraclePriceData, OraclePriceData } from '../oracles/types';
|
|
15
21
|
import {
|
|
@@ -22,6 +28,7 @@ import {
|
|
|
22
28
|
calculateMaxBaseAssetAmountToTrade,
|
|
23
29
|
calculateUpdatedAMM,
|
|
24
30
|
} from './amm';
|
|
31
|
+
import { calculateSizePremiumLiabilityWeight } from './margin';
|
|
25
32
|
|
|
26
33
|
export function isOrderRiskIncreasing(user: User, order: Order): boolean {
|
|
27
34
|
if (!isVariant(order.status, 'open')) {
|
|
@@ -411,3 +418,65 @@ export function calculateOrderBaseAssetAmount(
|
|
|
411
418
|
return BN.min(BN.max(existingBaseAssetAmount, ZERO), order.baseAssetAmount);
|
|
412
419
|
}
|
|
413
420
|
}
|
|
421
|
+
|
|
422
|
+
// ---------- inverse ----------
|
|
423
|
+
/**
|
|
424
|
+
* Invert the size-premium liability weight: given a target margin ratio (liability weight),
|
|
425
|
+
* return the max `size` (AMM_RESERVE_PRECISION units) that still yields <= target.
|
|
426
|
+
*
|
|
427
|
+
* Returns:
|
|
428
|
+
* - BN size (>=0) if bounded
|
|
429
|
+
* - null if impossible (target < liabilityWeight) OR imfFactor == 0 (unbounded)
|
|
430
|
+
*/
|
|
431
|
+
export function maxSizeForTargetLiabilityWeightBN(
|
|
432
|
+
target: BN,
|
|
433
|
+
imfFactor: BN,
|
|
434
|
+
liabilityWeight: BN
|
|
435
|
+
): BN | null {
|
|
436
|
+
if (target.lt(liabilityWeight)) return null;
|
|
437
|
+
if (imfFactor.isZero()) return null;
|
|
438
|
+
|
|
439
|
+
const base = liabilityWeight.muln(4).divn(5);
|
|
440
|
+
|
|
441
|
+
const denom = new BN(100_000)
|
|
442
|
+
.mul(SPOT_MARKET_IMF_PRECISION)
|
|
443
|
+
.div(MARGIN_PRECISION);
|
|
444
|
+
if (denom.isZero())
|
|
445
|
+
throw new Error('denom=0: bad precision/spotImfPrecision');
|
|
446
|
+
|
|
447
|
+
const allowedInc = target.gt(base) ? target.sub(base) : ZERO;
|
|
448
|
+
|
|
449
|
+
const maxSqrt = allowedInc.mul(denom).div(imfFactor);
|
|
450
|
+
|
|
451
|
+
if (maxSqrt.lte(ZERO)) {
|
|
452
|
+
const fitsZero = calculateSizePremiumLiabilityWeight(
|
|
453
|
+
ZERO,
|
|
454
|
+
imfFactor,
|
|
455
|
+
liabilityWeight,
|
|
456
|
+
MARGIN_PRECISION
|
|
457
|
+
).lte(target);
|
|
458
|
+
return fitsZero ? ZERO : null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let hi = maxSqrt.mul(maxSqrt).sub(ONE).divn(10);
|
|
462
|
+
if (hi.isNeg()) hi = ZERO;
|
|
463
|
+
|
|
464
|
+
let lo = ZERO;
|
|
465
|
+
while (lo.lt(hi)) {
|
|
466
|
+
const mid = lo.add(hi).add(ONE).divn(2); // upper mid to prevent infinite loop
|
|
467
|
+
if (
|
|
468
|
+
calculateSizePremiumLiabilityWeight(
|
|
469
|
+
mid,
|
|
470
|
+
imfFactor,
|
|
471
|
+
liabilityWeight,
|
|
472
|
+
MARGIN_PRECISION
|
|
473
|
+
).lte(target)
|
|
474
|
+
) {
|
|
475
|
+
lo = mid;
|
|
476
|
+
} else {
|
|
477
|
+
hi = mid.sub(ONE);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return lo;
|
|
482
|
+
}
|
|
File without changes
|