@deeeed/metamask-harness 0.94.1 → 0.94.3
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 +12 -0
- package/adapters/mobile/lib/idb-companion.sh +0 -2
- package/adapters/shared/process-identity.sh +4 -1
- package/library/actions/extension/perps/close_visible_position.mjs +11 -6
- package/library/actions/extension/perps/visible-mutation-identity.mjs +10 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.94.3 - 2026-10-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Extension testnet `close_visible_position` with `cleanup_only` binds one position by market and exact visible size. Fresh and resumed cleanup prove the accepted full close and zero target-market positions/orders without reconciling unrelated account-balance changes; the result names the cleaned market. Same-market orders still block, and mainnet keeps its global cleanup and balance guards.
|
|
10
|
+
|
|
11
|
+
## 0.94.2 - 2026-10-24
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Treat disappeared or reused companion processes as already stopped, preserving the unrelated PID and allowing repeated slot cleanup.
|
|
16
|
+
|
|
5
17
|
## 0.94.1 - 2026-10-22
|
|
6
18
|
|
|
7
19
|
### Fixed
|
|
@@ -41,8 +41,6 @@ stop_checkout_idb_companions() {
|
|
|
41
41
|
else
|
|
42
42
|
status=$?
|
|
43
43
|
[ "$status" -eq 1 ] && continue
|
|
44
|
-
printf 'Companion ownership is unknown. Next: mm-harness status --target "%s"\n' "$repo" >&2
|
|
45
|
-
return 1
|
|
46
44
|
fi
|
|
47
45
|
if ! stop_verified_process "$pid" "$identity" 'TERM KILL' 0.1 _idb_companion_owned "$repo" "$udid" "$pid" "$identity"; then
|
|
48
46
|
printf 'Companion %s cleanup was not confirmed. Next: mm-harness status --target "%s"\n' "$pid" "$repo" >&2
|
|
@@ -15,7 +15,9 @@ stop_verified_process() {
|
|
|
15
15
|
fi
|
|
16
16
|
for signal in $signals; do
|
|
17
17
|
if ! "$@"; then
|
|
18
|
-
|
|
18
|
+
current="$(process_identity "$pid")"
|
|
19
|
+
if [ -n "$current" ] && [ "$current" != "$identity" ]; then return 0; fi
|
|
20
|
+
if [ -z "$current" ] && ! kill -0 "$pid" 2>/dev/null; then return 0; fi
|
|
19
21
|
printf 'Process %s ownership changed before stop.\n' "$pid" >&2
|
|
20
22
|
return 1
|
|
21
23
|
fi
|
|
@@ -23,6 +25,7 @@ stop_verified_process() {
|
|
|
23
25
|
for attempt in $(seq 1 20); do
|
|
24
26
|
current="$(process_identity "$pid")"
|
|
25
27
|
if [ -z "$current" ] && ! kill -0 "$pid" 2>/dev/null; then return 0; fi
|
|
28
|
+
if [ -n "$current" ] && [ "$current" != "$identity" ]; then return 0; fi
|
|
26
29
|
if [ "$current" != "$identity" ]; then
|
|
27
30
|
printf 'Process %s identity changed during stop.\n' "$pid" >&2
|
|
28
31
|
return 1
|
|
@@ -208,9 +208,10 @@ async function prepareCloseModal(page, partialAmount) {
|
|
|
208
208
|
return confirmation;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
async function
|
|
211
|
+
async function proveCleanup(page, balanceBeforeBaseUnits, releaseBaseUnits, toleranceBaseUnits, market = null, timeoutMs = 20_000) {
|
|
212
212
|
await page.navigateHash('#/perps-home');
|
|
213
|
-
const state = await waitForStableGlobalPerpsHome(page, { timeoutMs, requireZero: true });
|
|
213
|
+
const state = await waitForStableGlobalPerpsHome(page, { timeoutMs, requireZero: true, market });
|
|
214
|
+
if (market) return { market, positionCount: 0, orderCount: 0 };
|
|
214
215
|
const expectedBalanceAfterBaseUnits = `${BigInt(balanceBeforeBaseUnits) + BigInt(releaseBaseUnits ?? '0')}`;
|
|
215
216
|
const balanceDeltaBaseUnits = absoluteDelta(state.balanceBaseUnits, expectedBalanceAfterBaseUnits);
|
|
216
217
|
if (BigInt(balanceDeltaBaseUnits) > BigInt(toleranceBaseUnits)) {
|
|
@@ -293,6 +294,7 @@ export async function closeVisiblePosition(input) {
|
|
|
293
294
|
roundingToleranceBaseUnits,
|
|
294
295
|
...(cleanupOnly ? { positionSize: fundingPositionSize, cleanupOnly: true } : {}),
|
|
295
296
|
});
|
|
297
|
+
const cleanupMarket = cleanupOnly && identity.network === 'testnet' ? market : null;
|
|
296
298
|
const closeBudget = trustedExternalMutationBudget(input, 'perps-close-position');
|
|
297
299
|
const existingReceipt = await readExternalMutationReceipt(input, identity);
|
|
298
300
|
if (existingReceipt) {
|
|
@@ -334,11 +336,12 @@ export async function closeVisiblePosition(input) {
|
|
|
334
336
|
if (releaseBaseUnits != null && !/^-?(?:0|[1-9][0-9]{0,29})$/u.test(String(releaseBaseUnits))) {
|
|
335
337
|
throw new Error('Resumed close receipt has an invalid close release amount.');
|
|
336
338
|
}
|
|
337
|
-
const cleanup = await
|
|
339
|
+
const cleanup = await proveCleanup(
|
|
338
340
|
page,
|
|
339
341
|
balanceBeforeBaseUnits,
|
|
340
342
|
releaseBaseUnits,
|
|
341
343
|
balanceToleranceBaseUnits,
|
|
344
|
+
cleanupMarket,
|
|
342
345
|
);
|
|
343
346
|
return {
|
|
344
347
|
success: true,
|
|
@@ -374,8 +377,9 @@ export async function closeVisiblePosition(input) {
|
|
|
374
377
|
let cleanupHomeBefore = null;
|
|
375
378
|
if (cleanupOnly) {
|
|
376
379
|
await page.navigateHash('#/perps-home');
|
|
377
|
-
cleanupHomeBefore = await waitForStableGlobalPerpsHome(page, { timeoutMs: 20_000, requireZero: false });
|
|
380
|
+
cleanupHomeBefore = await waitForStableGlobalPerpsHome(page, { timeoutMs: 20_000, requireZero: false, market: cleanupMarket });
|
|
378
381
|
if (cleanupHomeBefore.positionCount !== 1 || cleanupHomeBefore.orderCount !== 0) {
|
|
382
|
+
if (cleanupMarket) throw new Error('Cleanup-only close requires exactly one position and zero orders in the target market.');
|
|
379
383
|
throw new Error('Cleanup-only close requires exactly one primary position and globally zero orders.');
|
|
380
384
|
}
|
|
381
385
|
}
|
|
@@ -451,7 +455,7 @@ export async function closeVisiblePosition(input) {
|
|
|
451
455
|
dispatch: async () => {
|
|
452
456
|
let dispatchTarget;
|
|
453
457
|
try {
|
|
454
|
-
await requireCurrentVisibleFundingBalance(input, page, timeoutMs, fundingBalance.observedBaseUnits);
|
|
458
|
+
await requireCurrentVisibleFundingBalance(input, page, timeoutMs, cleanupMarket ? undefined : fundingBalance.observedBaseUnits);
|
|
455
459
|
assertTrustedExternalMutationTarget(input, page, identity);
|
|
456
460
|
const current = await readVisibleClosePosition(page, market);
|
|
457
461
|
const currentPosition = parsePositionDetail({ pair: `${market}-USD`, ...current });
|
|
@@ -522,11 +526,12 @@ export async function closeVisiblePosition(input) {
|
|
|
522
526
|
`Boolean(document.querySelector('[data-testid="perps-position-size-value"]'))`,
|
|
523
527
|
);
|
|
524
528
|
if (exactAbsent) {
|
|
525
|
-
const cleanup = await
|
|
529
|
+
const cleanup = await proveCleanup(
|
|
526
530
|
page,
|
|
527
531
|
balanceBeforeBaseUnits,
|
|
528
532
|
closeReleaseBaseUnits,
|
|
529
533
|
balanceToleranceBaseUnits,
|
|
534
|
+
cleanupMarket,
|
|
530
535
|
);
|
|
531
536
|
return {
|
|
532
537
|
success: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Parse and match visible Perps position and order identities using USD base-unit tolerances.
|
|
2
|
-
// Also wait for a stable
|
|
2
|
+
// Also wait for a stable Perps home view, with optional global or market-scoped cleanup checks.
|
|
3
3
|
const INTEGER = /^(0|[1-9][0-9]{0,29})$/u;
|
|
4
4
|
|
|
5
5
|
export function parseUsdBaseUnits(value) {
|
|
@@ -119,18 +119,21 @@ export async function waitForStableGlobalPerpsHome(page, options = {}) {
|
|
|
119
119
|
const timeoutMs = Number(options.timeoutMs ?? 20_000);
|
|
120
120
|
const stableMs = Number(options.stableMs ?? 1_000);
|
|
121
121
|
const requireZero = options.requireZero !== false;
|
|
122
|
+
const market = options.market ?? null;
|
|
122
123
|
const deadline = Date.now() + timeoutMs;
|
|
123
124
|
let stableSince = null;
|
|
124
125
|
let last = null;
|
|
125
126
|
while (Date.now() <= deadline) {
|
|
126
127
|
last = await page.evaluate(`(() => {
|
|
128
|
+
const market = ${JSON.stringify(market)};
|
|
127
129
|
const visible = (element) => {
|
|
128
130
|
if (!element) return false;
|
|
129
131
|
const rect = element.getBoundingClientRect();
|
|
130
132
|
const style = getComputedStyle(element);
|
|
131
133
|
return rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden';
|
|
132
134
|
};
|
|
133
|
-
const visibleCount = (selector) => [...document.querySelectorAll(selector)]
|
|
135
|
+
const visibleCount = (selector, childSelector = null) => [...document.querySelectorAll(selector)]
|
|
136
|
+
.filter(element => visible(element) && (!childSelector || element.querySelector(childSelector))).length;
|
|
134
137
|
const balance = document.querySelector('[data-testid="perps-balance-actions-available-value"]')
|
|
135
138
|
?? document.querySelector('[data-testid="perps-balance-dropdown-balance"]');
|
|
136
139
|
return {
|
|
@@ -139,8 +142,10 @@ export async function waitForStableGlobalPerpsHome(page, options = {}) {
|
|
|
139
142
|
?? document.querySelector('[data-testid="perps-view"]')),
|
|
140
143
|
loading: visibleCount('[data-testid$="-skeleton"], [data-testid*="loading"]') > 0,
|
|
141
144
|
marketCount: visibleCount('[data-testid="perps-explore-markets-row"]'),
|
|
142
|
-
positionCount: visibleCount('[data-testid
|
|
143
|
-
|
|
145
|
+
positionCount: visibleCount(market ? '[data-testid="position-card-' + market + '" i]'
|
|
146
|
+
: '[data-testid^="position-card-"]:not([data-testid^="position-card-roe-"])'),
|
|
147
|
+
orderCount: visibleCount('[data-testid^="order-card-"]', market
|
|
148
|
+
? '[data-testid="perps-token-logo-' + market.replace(/:/gu, '-') + '" i]' : null),
|
|
144
149
|
balance: visible(balance) ? String(balance.innerText || '').replace(/\\s+/gu, ' ').trim().slice(0, 200) : null,
|
|
145
150
|
};
|
|
146
151
|
})()`);
|
|
@@ -162,6 +167,7 @@ export async function waitForStableGlobalPerpsHome(page, options = {}) {
|
|
|
162
167
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
163
168
|
}
|
|
164
169
|
if (requireZero && last && (last.positionCount !== 0 || last.orderCount !== 0)) {
|
|
170
|
+
if (market) throw new Error(`Loaded Perps Home must have zero positions and orders in ${market} before cleanup.`);
|
|
165
171
|
throw new Error('Loaded Perps Home must have globally zero positions and orders before external mutation or cleanup.');
|
|
166
172
|
}
|
|
167
173
|
throw new Error('Loaded skeleton-free Perps Home did not become stable before timeout.');
|