@metamask-previews/perps-controller 20.1.0-preview-b4b0d7a09 → 21.0.0-preview-55fcf3ef7
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/CHANGELOG.md +14 -1
- package/README.md +46 -0
- package/dist/PerpsController.d.ts.map +1 -1
- package/dist/PerpsController.js +5 -1
- package/dist/PerpsController.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/MarketDataService.js +6 -6
- package/dist/services/MarketDataService.js.map +1 -1
- package/dist/services/RewardsIntegrationService.d.ts +13 -8
- package/dist/services/RewardsIntegrationService.d.ts.map +1 -1
- package/dist/services/RewardsIntegrationService.js +110 -20
- package/dist/services/RewardsIntegrationService.js.map +1 -1
- package/dist/services/TradingService.d.ts.map +1 -1
- package/dist/services/TradingService.js +37 -24
- package/dist/services/TradingService.js.map +1 -1
- package/dist/types/index.d.ts +36 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/subscriptionFeeWaiver.d.ts.map +1 -1
- package/dist/utils/subscriptionFeeWaiver.js +6 -3
- package/dist/utils/subscriptionFeeWaiver.js.map +1 -1
- package/package.json +1 -1
|
@@ -426,12 +426,13 @@ export class TradingService {
|
|
|
426
426
|
*
|
|
427
427
|
* @param options - The configuration options.
|
|
428
428
|
* @param options.provider - The perps provider instance.
|
|
429
|
-
* @param options.
|
|
429
|
+
* @param options.resolveFee - Resolves the fee after this operation acquires
|
|
430
|
+
* the fee context, so an expiring candidate cannot go stale in the queue.
|
|
430
431
|
* @param options.operation - The operation value.
|
|
431
432
|
* @returns The result of the operation.
|
|
432
433
|
*/
|
|
433
434
|
async #withFeeDiscount(options) {
|
|
434
|
-
const { provider,
|
|
435
|
+
const { provider, resolveFee, operation } = options;
|
|
435
436
|
const previous = this.#feeContextTail;
|
|
436
437
|
let release = () => undefined;
|
|
437
438
|
this.#feeContextTail = new Promise((resolve) => {
|
|
@@ -439,6 +440,7 @@ export class TradingService {
|
|
|
439
440
|
});
|
|
440
441
|
await previous;
|
|
441
442
|
try {
|
|
443
|
+
const feeResolution = await resolveFee();
|
|
442
444
|
if (provider.setUserFeeResolution) {
|
|
443
445
|
provider.setUserFeeResolution(feeResolution);
|
|
444
446
|
}
|
|
@@ -531,13 +533,6 @@ export class TradingService {
|
|
|
531
533
|
pay_token_symbol: paymentToken,
|
|
532
534
|
},
|
|
533
535
|
});
|
|
534
|
-
// Calculate fee discount at execution time (fresh, secure)
|
|
535
|
-
const feeResolution = await this.#calculateFeeDiscountWithMeasurement(this.#resolveOrderNotionalUsd(params));
|
|
536
|
-
this.#deps.debugLogger.log('TradingService: Fee resolution calculated', {
|
|
537
|
-
feeDiscountBips: feeResolution?.discountBips,
|
|
538
|
-
feeSource: feeResolution?.source,
|
|
539
|
-
hasDiscount: feeResolution?.discountBips !== undefined,
|
|
540
|
-
});
|
|
541
536
|
this.#deps.debugLogger.log('TradingService: Submitting order to provider', {
|
|
542
537
|
symbol: params.symbol,
|
|
543
538
|
orderType: params.orderType,
|
|
@@ -587,7 +582,15 @@ export class TradingService {
|
|
|
587
582
|
}, PERPS_CONSTANTS.PlaceOrderTimeoutMs);
|
|
588
583
|
const result = await this.#withFeeDiscount({
|
|
589
584
|
provider,
|
|
590
|
-
|
|
585
|
+
resolveFee: async () => {
|
|
586
|
+
const feeResolution = await this.#calculateFeeDiscountWithMeasurement(this.#resolveOrderNotionalUsd(params), provider, context, params.providerId);
|
|
587
|
+
this.#deps.debugLogger.log('TradingService: Fee resolution calculated', {
|
|
588
|
+
feeDiscountBips: feeResolution?.discountBips,
|
|
589
|
+
feeSource: feeResolution?.source,
|
|
590
|
+
hasDiscount: feeResolution?.discountBips !== undefined,
|
|
591
|
+
});
|
|
592
|
+
return feeResolution;
|
|
593
|
+
},
|
|
591
594
|
operation: () => provider.placeOrder(params),
|
|
592
595
|
});
|
|
593
596
|
if (orderSubmissionThresholdTimeoutId !== undefined) {
|
|
@@ -1155,10 +1158,13 @@ export class TradingService {
|
|
|
1155
1158
|
* priced. This is what lets the subscription source resolve to a blended
|
|
1156
1159
|
* rate: omitting it resolves every bounded allowance as a full waiver,
|
|
1157
1160
|
* charging 0 bips on an order the preview quoted a blend for.
|
|
1161
|
+
* @param provider - The actual provider receiving this submission.
|
|
1162
|
+
* @param context - The submission context, including its network.
|
|
1163
|
+
* @param providerId - Explicit route, when the caller supplied one.
|
|
1158
1164
|
* @returns The resolved fee, or undefined when controller dependencies are
|
|
1159
1165
|
* unavailable.
|
|
1160
1166
|
*/
|
|
1161
|
-
async #calculateFeeDiscountWithMeasurement(orderNotionalUsd) {
|
|
1167
|
+
async #calculateFeeDiscountWithMeasurement(orderNotionalUsd, provider, context, providerId) {
|
|
1162
1168
|
// Check if controller dependencies are available
|
|
1163
1169
|
if (!this.#controllerDeps) {
|
|
1164
1170
|
this.#deps.debugLogger.log('TradingService: Controller dependencies not set, skipping fee discount');
|
|
@@ -1169,7 +1175,10 @@ export class TradingService {
|
|
|
1169
1175
|
// The notional is what lets the subscription source resolve to a blended
|
|
1170
1176
|
// rate. Submitting without it would resolve every bounded allowance as a
|
|
1171
1177
|
// full waiver, charging 0 bips on an order the preview quoted a blend for.
|
|
1172
|
-
const resolution = await rewardsIntegrationService.resolveFee(orderNotionalUsd
|
|
1178
|
+
const resolution = await rewardsIntegrationService.resolveFee(orderNotionalUsd, {
|
|
1179
|
+
providerId: provider.getWriteProviderId?.(providerId) ?? provider.protocolId,
|
|
1180
|
+
isTestnet: context.tracingContext.isTestnet,
|
|
1181
|
+
});
|
|
1173
1182
|
const orderExecutionFeeDiscountDuration = this.#deps.performance.now() - orderExecutionFeeDiscountStartTime;
|
|
1174
1183
|
// Record measurement
|
|
1175
1184
|
this.#deps.tracer.setMeasurement(PerpsMeasurementName.PerpsRewardsOrderExecutionFeeDiscountApiCall, orderExecutionFeeDiscountDuration, 'millisecond');
|
|
@@ -1214,12 +1223,10 @@ export class TradingService {
|
|
|
1214
1223
|
orderPrice: params.newOrder.price ?? '',
|
|
1215
1224
|
},
|
|
1216
1225
|
});
|
|
1217
|
-
// Calculate fee discount only if required dependencies are available
|
|
1218
|
-
const feeResolution = await this.#calculateFeeDiscountWithMeasurement(this.#resolveOrderNotionalUsd(params.newOrder));
|
|
1219
1226
|
// Execute order edit with fee discount management
|
|
1220
1227
|
const result = await this.#withFeeDiscount({
|
|
1221
1228
|
provider,
|
|
1222
|
-
|
|
1229
|
+
resolveFee: () => this.#calculateFeeDiscountWithMeasurement(this.#resolveOrderNotionalUsd(params.newOrder), provider, context, params.newOrder.providerId),
|
|
1223
1230
|
operation: () => provider.editOrder(params),
|
|
1224
1231
|
});
|
|
1225
1232
|
const completionDuration = this.#deps.performance.now() - startTime;
|
|
@@ -1628,7 +1635,7 @@ export class TradingService {
|
|
|
1628
1635
|
// only a symbol, so `params` alone prices it as undefined and the resolver
|
|
1629
1636
|
// would quote a full waiver on an order the preview blended. The position
|
|
1630
1637
|
// loaded above is the authoritative notional for exactly that case.
|
|
1631
|
-
const
|
|
1638
|
+
const closeNotionalUsd = this.#resolveOrderNotionalUsd({
|
|
1632
1639
|
...params,
|
|
1633
1640
|
// A partial close names a size but often no price; the position's own
|
|
1634
1641
|
// value per unit prices it. A full close names neither, and falls back
|
|
@@ -1637,11 +1644,11 @@ export class TradingService {
|
|
|
1637
1644
|
}) ??
|
|
1638
1645
|
this.#resolveOrderNotionalUsd({
|
|
1639
1646
|
usdAmount: position?.positionValue,
|
|
1640
|
-
})
|
|
1647
|
+
});
|
|
1641
1648
|
// Execute position close with fee discount management
|
|
1642
1649
|
result = await this.#withFeeDiscount({
|
|
1643
1650
|
provider,
|
|
1644
|
-
|
|
1651
|
+
resolveFee: () => this.#calculateFeeDiscountWithMeasurement(closeNotionalUsd, provider, context, params.providerId),
|
|
1645
1652
|
operation: () => provider.closePosition(params),
|
|
1646
1653
|
});
|
|
1647
1654
|
const completionDuration = this.#deps.performance.now() - startTime;
|
|
@@ -1758,10 +1765,13 @@ export class TradingService {
|
|
|
1758
1765
|
if (provider.closePositions) {
|
|
1759
1766
|
// The batch submits under one builder context, so its notional is the
|
|
1760
1767
|
// sum of the positions it will close, not any single one of them.
|
|
1761
|
-
const
|
|
1768
|
+
const batchCloseNotionalUsd = await this.#resolveBatchCloseNotionalUsd({
|
|
1769
|
+
params,
|
|
1770
|
+
provider,
|
|
1771
|
+
});
|
|
1762
1772
|
operationResult = await this.#withFeeDiscount({
|
|
1763
1773
|
provider,
|
|
1764
|
-
|
|
1774
|
+
resolveFee: () => this.#calculateFeeDiscountWithMeasurement(batchCloseNotionalUsd, provider, context),
|
|
1765
1775
|
operation: async () => {
|
|
1766
1776
|
if (!provider.closePositions) {
|
|
1767
1777
|
throw new Error('closePositions method not available');
|
|
@@ -1964,11 +1974,10 @@ export class TradingService {
|
|
|
1964
1974
|
pricedTriggerNotionalsUsd.length === triggerNotionalsUsd.length
|
|
1965
1975
|
? Math.max(...pricedTriggerNotionalsUsd)
|
|
1966
1976
|
: undefined;
|
|
1967
|
-
const feeResolution = await this.#calculateFeeDiscountWithMeasurement(tpslNotionalUsd);
|
|
1968
1977
|
// Execute with fee discount management
|
|
1969
1978
|
result = await this.#withFeeDiscount({
|
|
1970
1979
|
provider,
|
|
1971
|
-
|
|
1980
|
+
resolveFee: () => this.#calculateFeeDiscountWithMeasurement(tpslNotionalUsd, provider, context, params.providerId),
|
|
1972
1981
|
operation: () => provider.updatePositionTPSL(params),
|
|
1973
1982
|
});
|
|
1974
1983
|
if (result.success) {
|
|
@@ -2207,12 +2216,17 @@ export class TradingService {
|
|
|
2207
2216
|
// Use 2x position size: 1x to close current position + 1x to open opposite position.
|
|
2208
2217
|
// Do not pass the position entry price as currentPrice: the provider must fetch
|
|
2209
2218
|
// live market data for validation and IOC pricing.
|
|
2219
|
+
// Carry the position venue so aggregated placeOrder reaches the same
|
|
2220
|
+
// provider the fee grant below is scoped to.
|
|
2210
2221
|
const orderParams = {
|
|
2211
2222
|
symbol: position.symbol,
|
|
2212
2223
|
isBuy: oppositeDirection,
|
|
2213
2224
|
size: flipSize.toString(),
|
|
2214
2225
|
orderType: 'market',
|
|
2215
2226
|
leverage: position.leverage?.value,
|
|
2227
|
+
...(position.providerId === undefined
|
|
2228
|
+
? {}
|
|
2229
|
+
: { providerId: position.providerId }),
|
|
2216
2230
|
// Lighter placeOrder otherwise logs order_management/place_order and
|
|
2217
2231
|
// marks the result reported, which drops this flip from flip filters.
|
|
2218
2232
|
// Close already suppresses that provider report. One Sentry event remains.
|
|
@@ -2237,11 +2251,10 @@ export class TradingService {
|
|
|
2237
2251
|
const flipNotionalUsd = this.#resolveOrderNotionalUsd({
|
|
2238
2252
|
usdAmount: position.positionValue,
|
|
2239
2253
|
});
|
|
2240
|
-
const feeResolution = await this.#calculateFeeDiscountWithMeasurement(flipNotionalUsd === undefined ? undefined : flipNotionalUsd * 2);
|
|
2241
2254
|
// Place flip order (HyperLiquid handles margin transfer automatically)
|
|
2242
2255
|
const result = await this.#withFeeDiscount({
|
|
2243
2256
|
provider,
|
|
2244
|
-
|
|
2257
|
+
resolveFee: () => this.#calculateFeeDiscountWithMeasurement(flipNotionalUsd === undefined ? undefined : flipNotionalUsd * 2, provider, context, position.providerId),
|
|
2245
2258
|
operation: () => provider.placeOrder(orderParams),
|
|
2246
2259
|
});
|
|
2247
2260
|
const completionDuration = this.#deps.performance.now() - startTime;
|