@metamask-previews/perps-controller 22.1.0-preview-8b7a55483 → 22.1.0-preview-0439f4dd7
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
CHANGED
|
@@ -7,15 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Changed
|
|
11
|
-
|
|
12
|
-
- Bump `uuid` from `^14.0.0` to `^14.0.1` ([#11179](https://github.com/MetaMask/core/pull/11179))
|
|
13
|
-
|
|
14
|
-
### Fixed
|
|
15
|
-
|
|
16
|
-
- Prevent HyperLiquid `userFills` from going silent after a WebSocket reconnect when an in-flight subscribe races with `restoreSubscriptions` (same pending + generation pattern TWAP already uses) ([#11160](https://github.com/MetaMask/core/pull/11160))
|
|
17
|
-
- Without this, live fills can keep attaching to a dead pre-reconnect listener (MetaMask/metamask-extension#47287).
|
|
18
|
-
|
|
19
10
|
## [22.1.0]
|
|
20
11
|
|
|
21
12
|
### Added
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HyperLiquidSubscriptionService.d.ts","sourceRoot":"","sources":["../../src/services/HyperLiquidSubscriptionService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,mBAAmB,EAKnB,gBAAgB,EAMjB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,+BAA+B,CAAC;AAGvC,OAAO,KAAK,EAEV,QAAQ,EACR,SAAS,EACT,KAAK,EAEL,SAAS,EACT,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EAGxB,yBAAyB,EAG1B,MAAM,mBAAmB,CAAC;AAgC3B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAE9E;;;GAGG;AACH,KAAK,kBAAkB,GAAG,IAAI,CAC5B,wBAAwB,EACxB,qBAAqB,GAAG,2BAA2B,CACpD,CAAC;AA0BF;;;GAGG;AACH,qBAAa,8BAA8B;;
|
|
1
|
+
{"version":3,"file":"HyperLiquidSubscriptionService.d.ts","sourceRoot":"","sources":["../../src/services/HyperLiquidSubscriptionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -92,20 +92,6 @@ export class HyperLiquidSubscriptionService {
|
|
|
92
92
|
#pendingBboPromises = new Map();
|
|
93
93
|
// Order fill subscriptions keyed by accountId (normalized: undefined -> 'default')
|
|
94
94
|
#orderFillSubscriptions = new Map();
|
|
95
|
-
// In-flight userFills subscribe calls, so concurrent subscribers / reconnect
|
|
96
|
-
// share one socket open instead of racing two opens onto the same map key.
|
|
97
|
-
#pendingOrderFillSubscriptions = new Map();
|
|
98
|
-
// Bumped per account by teardown and reconnect so an in-flight open for
|
|
99
|
-
// that account discards its subscription instead of rehydrating a cleared
|
|
100
|
-
// map (same race TWAP already guards; without it live fills can stick to a
|
|
101
|
-
// dead pre-reconnect listener — MetaMask/metamask-extension#47287).
|
|
102
|
-
#orderFillSubscriptionGenerations = new Map();
|
|
103
|
-
#orderFillGeneration(normalizedAccountId) {
|
|
104
|
-
return this.#orderFillSubscriptionGenerations.get(normalizedAccountId) ?? 0;
|
|
105
|
-
}
|
|
106
|
-
#bumpOrderFillGeneration(normalizedAccountId) {
|
|
107
|
-
this.#orderFillSubscriptionGenerations.set(normalizedAccountId, this.#orderFillGeneration(normalizedAccountId) + 1);
|
|
108
|
-
}
|
|
109
95
|
// TWAP subscriptions keyed by accountId (normalized: undefined -> 'default')
|
|
110
96
|
#twapOrderSubscriptions = new Map();
|
|
111
97
|
// In-flight TWAP subscribe calls, so concurrent subscribers share one socket
|
|
@@ -1968,10 +1954,6 @@ export class HyperLiquidSubscriptionService {
|
|
|
1968
1954
|
});
|
|
1969
1955
|
this.#orderFillSubscriptions.delete(normalizedAccountId);
|
|
1970
1956
|
}
|
|
1971
|
-
// Invalidate any open still in flight for this account: without this
|
|
1972
|
-
// its continuation installs a live socket nobody is listening to.
|
|
1973
|
-
this.#bumpOrderFillGeneration(normalizedAccountId);
|
|
1974
|
-
this.#pendingOrderFillSubscriptions.delete(normalizedAccountId);
|
|
1975
1957
|
}
|
|
1976
1958
|
};
|
|
1977
1959
|
}
|
|
@@ -1989,71 +1971,19 @@ export class HyperLiquidSubscriptionService {
|
|
|
1989
1971
|
if (this.#orderFillSubscriptions.has(normalizedAccountId)) {
|
|
1990
1972
|
return;
|
|
1991
1973
|
}
|
|
1992
|
-
|
|
1993
|
-
// subscribe + reconnect do not race two userFills opens onto one key.
|
|
1994
|
-
// The owner of the pending open logs its failure, so swallow it here and
|
|
1995
|
-
// fall through to open our own. Re-read after each wait: several waiters
|
|
1996
|
-
// resume together and only the first should start the replacement open.
|
|
1997
|
-
let pending = this.#pendingOrderFillSubscriptions.get(normalizedAccountId);
|
|
1998
|
-
while (pending) {
|
|
1999
|
-
await pending.catch(() => undefined);
|
|
2000
|
-
if (this.#orderFillSubscriptions.has(normalizedAccountId)) {
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
pending = this.#pendingOrderFillSubscriptions.get(normalizedAccountId);
|
|
2004
|
-
}
|
|
2005
|
-
// A discarded open resolves without registering; if the last subscriber
|
|
2006
|
-
// left meanwhile, opening now would cache frames nobody is listening to.
|
|
2007
|
-
const subscribers = this.#orderFillSubscribers.get(normalizedAccountId);
|
|
2008
|
-
if (!subscribers || subscribers.size === 0) {
|
|
2009
|
-
return;
|
|
2010
|
-
}
|
|
2011
|
-
// Captured once here, spanning the whole open including the client
|
|
2012
|
-
// handshake. Capturing inside the open would let a bump that lands during
|
|
2013
|
-
// the handshake be adopted as the baseline and silently forgotten.
|
|
2014
|
-
const startGeneration = this.#orderFillGeneration(normalizedAccountId);
|
|
2015
|
-
const subscriptionPromise = this.#createOrderFillSubscription(normalizedAccountId, accountId, startGeneration);
|
|
2016
|
-
this.#pendingOrderFillSubscriptions.set(normalizedAccountId, subscriptionPromise);
|
|
2017
|
-
try {
|
|
2018
|
-
await subscriptionPromise;
|
|
2019
|
-
}
|
|
2020
|
-
finally {
|
|
2021
|
-
// Delete by identity: a reconnect may have cleared the map and installed
|
|
2022
|
-
// a newer open behind this one, which must not be evicted here.
|
|
2023
|
-
if (this.#pendingOrderFillSubscriptions.get(normalizedAccountId) ===
|
|
2024
|
-
subscriptionPromise) {
|
|
2025
|
-
this.#pendingOrderFillSubscriptions.delete(normalizedAccountId);
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
/**
|
|
2030
|
-
* Open one userFills subscription and register it for teardown.
|
|
2031
|
-
*
|
|
2032
|
-
* @param normalizedAccountId - Map key for this account ('default' when unset).
|
|
2033
|
-
* @param accountId - Optional CAIP account ID to resolve the address from.
|
|
2034
|
-
* @param startGeneration - This account's generation when the open began.
|
|
2035
|
-
* @returns A promise that resolves once the subscription is registered.
|
|
2036
|
-
*/
|
|
2037
|
-
async #createOrderFillSubscription(normalizedAccountId, accountId, startGeneration) {
|
|
2038
|
-
// Bootstrap the client inline rather than recursing: a recursive call
|
|
2039
|
-
// would re-read the generation after the handshake and adopt a bump that
|
|
2040
|
-
// landed during it as its own baseline.
|
|
2041
|
-
let subscriptionClient = this.#clientService.getSubscriptionClient();
|
|
1974
|
+
const subscriptionClient = this.#clientService.getSubscriptionClient();
|
|
2042
1975
|
if (!subscriptionClient) {
|
|
2043
1976
|
await this.#clientService.ensureSubscriptionClient(this.#walletService.createWalletAdapter());
|
|
2044
|
-
|
|
2045
|
-
if (!
|
|
1977
|
+
const client = this.#clientService.getSubscriptionClient();
|
|
1978
|
+
if (!client) {
|
|
2046
1979
|
throw new Error('SubscriptionClient not available');
|
|
2047
1980
|
}
|
|
1981
|
+
await this.#ensureOrderFillISubscription(accountId);
|
|
1982
|
+
return;
|
|
2048
1983
|
}
|
|
2049
1984
|
const userAddress = await this.#walletService.getUserAddressWithDefault(accountId);
|
|
2050
1985
|
// userFills returns a Promise<ISubscription>, need to await it
|
|
2051
1986
|
const subscription = await subscriptionClient.userFills({ user: userAddress }, (data) => {
|
|
2052
|
-
// A frame queued before an unsubscribe or reconnect must not mutate
|
|
2053
|
-
// the replacement's cache or reach its subscribers.
|
|
2054
|
-
if (startGeneration !== this.#orderFillGeneration(normalizedAccountId)) {
|
|
2055
|
-
return;
|
|
2056
|
-
}
|
|
2057
1987
|
// Build a Map for O(1) lookup instead of O(n) find per fill
|
|
2058
1988
|
const orderMap = new Map();
|
|
2059
1989
|
if (this.#cachedOrders) {
|
|
@@ -2116,17 +2046,6 @@ export class HyperLiquidSubscriptionService {
|
|
|
2116
2046
|
});
|
|
2117
2047
|
}
|
|
2118
2048
|
});
|
|
2119
|
-
// Teardown, clearAll, or a reconnect may have run while this open was in
|
|
2120
|
-
// flight. Rehydrating the map here would leave a socket with no
|
|
2121
|
-
// subscribers, or clobber the live replacement — so drop ours instead.
|
|
2122
|
-
const subscribers = this.#orderFillSubscribers.get(normalizedAccountId);
|
|
2123
|
-
if (startGeneration !== this.#orderFillGeneration(normalizedAccountId) ||
|
|
2124
|
-
this.#orderFillSubscriptions.has(normalizedAccountId) ||
|
|
2125
|
-
!subscribers ||
|
|
2126
|
-
subscribers.size === 0) {
|
|
2127
|
-
subscription.unsubscribe().catch(() => undefined);
|
|
2128
|
-
return;
|
|
2129
|
-
}
|
|
2130
2049
|
this.#orderFillSubscriptions.set(normalizedAccountId, subscription);
|
|
2131
2050
|
}
|
|
2132
2051
|
/**
|
|
@@ -3624,19 +3543,8 @@ export class HyperLiquidSubscriptionService {
|
|
|
3624
3543
|
}
|
|
3625
3544
|
// Re-establish order fill subscriptions if there are fill subscribers
|
|
3626
3545
|
if (this.#orderFillSubscribers.size > 0) {
|
|
3627
|
-
//
|
|
3628
|
-
// each account's generation first so a pre-reconnect open still in
|
|
3629
|
-
// flight discards itself instead of occupying the map ahead of its
|
|
3630
|
-
// replacement (MetaMask/metamask-extension#47287).
|
|
3631
|
-
for (const normalizedAccountId of new Set([
|
|
3632
|
-
...this.#orderFillSubscribers.keys(),
|
|
3633
|
-
...this.#orderFillSubscriptions.keys(),
|
|
3634
|
-
...this.#pendingOrderFillSubscriptions.keys(),
|
|
3635
|
-
])) {
|
|
3636
|
-
this.#bumpOrderFillGeneration(normalizedAccountId);
|
|
3637
|
-
}
|
|
3546
|
+
// Clear existing subscription references (they're dead after reconnection)
|
|
3638
3547
|
this.#orderFillSubscriptions.clear();
|
|
3639
|
-
this.#pendingOrderFillSubscriptions.clear();
|
|
3640
3548
|
// Re-establish subscriptions for all accountIds with subscribers
|
|
3641
3549
|
// Note: normalizedAccountId is 'default' for undefined, need to convert back
|
|
3642
3550
|
const normalizedAccountIds = Array.from(this.#orderFillSubscribers.keys());
|
|
@@ -3808,15 +3716,6 @@ export class HyperLiquidSubscriptionService {
|
|
|
3808
3716
|
])) {
|
|
3809
3717
|
this.#bumpTwapOrderGeneration(normalizedAccountId);
|
|
3810
3718
|
}
|
|
3811
|
-
// Same for order fills: bump before subscriber keys are cleared so an
|
|
3812
|
-
// in-flight userFills open cannot rehydrate after clearAll.
|
|
3813
|
-
for (const normalizedAccountId of new Set([
|
|
3814
|
-
...this.#orderFillSubscribers.keys(),
|
|
3815
|
-
...this.#orderFillSubscriptions.keys(),
|
|
3816
|
-
...this.#pendingOrderFillSubscriptions.keys(),
|
|
3817
|
-
])) {
|
|
3818
|
-
this.#bumpOrderFillGeneration(normalizedAccountId);
|
|
3819
|
-
}
|
|
3820
3719
|
// Clear all local subscriber collections
|
|
3821
3720
|
this.#priceSubscribers.clear();
|
|
3822
3721
|
this.#positionSubscribers.clear();
|
|
@@ -3827,14 +3726,13 @@ export class HyperLiquidSubscriptionService {
|
|
|
3827
3726
|
this.#accountSubscribers.clear();
|
|
3828
3727
|
this.#marketDataSubscribers.clear();
|
|
3829
3728
|
this.#orderBookSubscribers.clear();
|
|
3830
|
-
// Clear order fill subscriptions
|
|
3729
|
+
// Clear order fill subscriptions
|
|
3831
3730
|
this.#orderFillSubscriptions.forEach((subscription) => {
|
|
3832
3731
|
subscription.unsubscribe().catch(() => {
|
|
3833
3732
|
// Ignore errors during cleanup
|
|
3834
3733
|
});
|
|
3835
3734
|
});
|
|
3836
3735
|
this.#orderFillSubscriptions.clear();
|
|
3837
|
-
this.#pendingOrderFillSubscriptions.clear();
|
|
3838
3736
|
// Clear TWAP subscriptions; generations were bumped above, before the
|
|
3839
3737
|
// subscriber keys naming those accounts were cleared.
|
|
3840
3738
|
this.#cachedTwapOrders.clear();
|