Skip to content

test(perps): add client recipe validation smoke - #8893

Closed
abretonc7s wants to merge 33 commits into
mainfrom
feat/perps-e2e-validation
Closed

test(perps): add client recipe validation smoke#8893
abretonc7s wants to merge 33 commits into
mainfrom
feat/perps-e2e-validation

Conversation

@abretonc7s

@abretonc7sabretonc7s commented May 25, 2026

Copy link
Copy Markdown
Contributor

Explanation

Adds a minimal headless recipe validation path for @metamask/perps-controller that matches the intended Mobile/Extension boundary: a client calls PerpsController:* messenger actions, and the controller delegates to its services/providers.

This replaces the previous standalone HyperLiquid e2e scripts. The recipe/test no longer imports or constructs HyperLiquid SDK clients directly.

Changes

  • Removes direct packages/perps-controller/e2e/* HyperLiquid scripts.
  • Adds @farmslot/recipe-harness as a dev dependency.
  • Adds packages/perps-controller/recipes/client-smoke.recipe.json plus a small official-action manifest.
  • Adds packages/perps-controller/recipes/README.md explaining recipes as structured agentic-loop validation compatible with Extension/Mobile validation usage.
  • Keeps package scripts unchanged; the recipe can be invoked explicitly through yarn workspace @metamask/perps-controller exec farmslot-recipe ....
  • Adds PerpsController.client-smoke.test.ts, which simulates an installed MetaMask client dispatching controller messenger actions:
    • PerpsController:getMarkets
    • PerpsController:getPositions
    • PerpsController:getAccountState
    • PerpsController:placeOrder
    • PerpsController:cancelOrder

Farmslot dependency

Already published under the @farmslot npm scope:

  • @farmslot/protocol@0.5.0
  • @farmslot/recipe-harness@0.1.0

Validation

  • yarn install --immutable
  • NODE_OPTIONS=--experimental-vm-modules yarn workspace @metamask/perps-controller jest --runInBand --no-coverage --runTestsByPath tests/src/PerpsController.client-smoke.test.ts --verbose
  • yarn workspace @metamask/perps-controller exec farmslot-recipe run recipes/client-smoke.recipe.json --artifacts-dir ../../.recipe-artifacts/perps-controller/client-smoke --action-manifest recipes/headless.action-manifest.json --project-root .
    • recipe status: pass
  • yarn lint
  • pre-push hook via normal git push

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Test-only and dev-tooling changes; no published controller API or runtime behavior changes.

Overview
Adds a Farmslot recipe path for @metamask/perps-controller so agentic validation can run repeatable headless checks and collect Jest JSON evidence under gitignored repo-root temp/, without changing package scripts.

The recipe (client-smoke.recipe.json) runs PerpsController.client-smoke.test.ts, asserts exit code and Jest report fields, and indexes artifacts. @farmslot/recipe-harness is added as a dev-only dependency with a small official-action manifest and README for explicit farmslot-recipe invocation.

The new smoke test models Mobile/Extension usage: a client dispatches PerpsController:* messenger actions (getMarkets, getPositions, getAccountState, placeOrder, cancelOrder) through a dispatching mock messenger, with services/providers mocked and no direct HyperLiquid/protocol SDK usage.

Reviewed by Cursor Bugbot for commit 17fb836. Bugbot is set up for automated code reviews on this repo. Configure here.

@abretonc7s
abretonc7s requested a review from a team as a code ownerMay 25, 2026 23:47
@abretonc7s
abretonc7s requested a review from a team as a code ownerMay 26, 2026 00:29
@socket-security

socket-securityBot commented May 26, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

DiffPackageSupply Chain
Security
VulnerabilityQualityMaintenanceLicense
Added@​farmslot/​recipe-harness@​0.2.07710010088100

View full report

@socket-security

socket-securityBot commented May 26, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

Comment threadpackages/perps-controller/e2e/trading-lifecycle.ts Outdated
Comment threadpackages/perps-controller/e2e/trading-lifecycle.ts Outdated
The close order's slippage price was computed from the mid price fetched
at script start (step 1), but the close happens at step 6 after leverage
setup, order placement, and optional TP/SL — potentially minutes later.
Stale price could cause the IOC close to miss and leave the position open.
…efore close
- Change TP/SL order grouping from 'na' to 'positionTpsl' so HyperLiquid
properly associates trigger orders with the open position.
- Re-fetch mid price immediately before computing close order price to avoid
stale price from script start causing IOC close to not fill.
@abretonc7s

Copy link
Copy Markdown
ContributorAuthor

Automated pr-complete run — #8893

MetricValue
Run6051ea71
Duration?
Modelclaude/opus
Nudges0
Worker report

PR #8893 — Comments Report

PR Comments/Checks Addressed

cursor[bot] Review Comments (2 issues)

1. TP/SL orders use wrong grouping value (Medium Severity)

  • File: packages/perps-controller/e2e/trading-lifecycle.ts:213
  • Issue: TP/SL orders used grouping: 'na' instead of grouping: 'positionTpsl'. HyperLiquid requires positionTpsl grouping to associate trigger orders with an existing position.
  • Fix: Changed grouping: 'na'grouping: 'positionTpsl' on the TP/SL order call.

2. Close order uses stale price from script start (Medium Severity)

  • File: packages/perps-controller/e2e/trading-lifecycle.ts:225
  • Issue: Close order derived slippage price from midPrice fetched at script start (step 1), but the close happens much later (step 6). If the market moves >3% during the window, the IOC close order won't fill.
  • Fix: Added a fresh info.allMids() call immediately before computing closePrice.

Bot Comments (not actionable)

  • socket-security[bot]: Dependency audit — informational only (viem update), no action needed.

CI Checks

  • Check changelog: FAILURE — pre-existing; e2e scripts in e2e/ do not require changelog entries (not published).
  • All lint, build, test, and security checks: SUCCESS.

Files Changed

FileChange
packages/perps-controller/e2e/trading-lifecycle.tsFixed TP/SL grouping (napositionTpsl); re-fetch mid price before close order

Downstream Compatibility Assessment

No impact. Changes are limited to e2e/trading-lifecycle.ts, which is an e2e test script outside the published src/ directory. No public API changes, no type changes, no exports affected. Mobile and Extension clients are not impacted.

Validation Results

  • Unit tests: 58 suites, 1725 passed, 40 skipped — all green.
  • Build: Pre-existing TS6305 errors from unbuilt dependency outputs (not related to this change).
  • Recipe: No inherited recipe artifact present; skipped.

@abretonc7s
abretonc7s enabled auto-merge May 27, 2026 03:47
Comment threadpackages/perps-controller/e2e/limit-orders.ts Outdated
Comment threadpackages/perps-controller/e2e/limit-orders.ts Outdated
The e2e limit-order script could continue into cancellation and final removal verification after failing to extract a resting order id. The final removal check then compared open orders against null, which made the cleanup verification look successful even though the script did not have a concrete exchange order to cancel or verify.\n\nThe script now records an explicit failure when no resting order id is extracted and exits with the accumulated runner result before any cancellation/removal checks run.\n\nConstraint: Review feedback targeted the e2e script behavior only; keep the production controller surface unchanged.\nRejected: Leave the final null comparison in place | it preserves the misleading cleanup signal reported by Bugbot.\nConfidence: high\nScope-risk: narrow\nTested: yarn workspace @metamask/perps-controller test --bail --no-coverage\nTested: yarn exec eslint packages/perps-controller/e2e/limit-orders.ts\nNot-tested: Live trading scenario execution; HL_E2E_PRIVATE_KEY is not configured in this slot.
## Explanation
Adds a self-contained Recipe v1 headless example for
`@metamask/perps-controller`, stacked on #8893.
This version avoids using the direct HyperLiquid e2e scripts as the
proof path. The recipes now exercise public `PerpsController` methods
through a headless in-memory provider so the proof demonstrates
controller/service/provider orchestration without UI, wallet
credentials, or direct exchange calls.
Included recipes:
- `market-data.recipe.json`: calls `PerpsController.getMarkets` and
asserts the returned market data.
- `trading-lifecycle.recipe.json`: calls `placeOrder`,
`updatePositionTPSL`, and `closePosition` and asserts each controller
result.
Also adds local ignore entries for AI/temp artifacts so the pre-push
lint hook does not scan local-only state directories.
## Validation
- `yarn eslint eslint.config.mjs
packages/perps-controller/recipe-v1/runner.ts`
- `yarn lint:misc --check packages/perps-controller/package.json
packages/perps-controller/tsconfig.json
packages/perps-controller/recipe-v1/manifests/perps.action-manifest.json
packages/perps-controller/recipe-v1/recipes/market-data.recipe.json
packages/perps-controller/recipe-v1/recipes/trading-lifecycle.recipe.json`
- `yarn workspace @metamask/perps-controller recipe:market-data`
- `yarn workspace @metamask/perps-controller recipe:trading-lifecycle`
- `git push` pre-push hook: `yarn lint` passed
## References
- Stacked on #8893
## Checklist
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated changelogs for packages I've changed, or this change
is internal/dev-only tooling
- [ ] I've introduced breaking changes and prepared consumer PRs if
needed
Comment threadpackages/perps-controller/e2e/trading-lifecycle.ts Outdated
Comment threadpackages/perps-controller/recipe-v1/runner.ts Outdated
Comment threadpackages/perps-controller/e2e/trading-lifecycle.ts Outdated
Comment threadpackages/perps-controller/e2e/trading-lifecycle.ts Outdated
@abretonc7sChatGPT Codex Connector

Copy link
Copy Markdown
ContributorAuthor

Heads up: PR #8955 was merged into this branch accidentally and then reverted in 93bc64bf2 because that Recipe v1 shape was too intrusive for core.

Replacement direction is to keep core minimal and consume the reusable Farmslot headless harness once @farmslot/protocol + @farmslot/recipe-harness are publishable, instead of embedding a full runner under packages/perps-controller/recipe-v1.

@abretonc7sabretonc7s changed the title feat(perps): add e2e validation scripts for HyperLiquid API testingtest(perps): add client recipe validation smokeJun 2, 2026
Comment threadpackages/perps-controller/package.json Outdated
Comment threadpackages/perps-controller/tsconfig.json Outdated
@abretonc7sChatGPT Codex Connector

Copy link
Copy Markdown
ContributorAuthor

@SocketSecurity ignore npm/@farmslot/recipe-harness@0.1.0

Reviewed for this PR: @farmslot/recipe-harness is a dev-only validation runner used by an explicit yarn workspace @metamask/perps-controller exec farmslot-recipe ... command, not production/runtime code. The command execution sink is expected for a trusted, checked-in recipe runner; this PR's recipe runs only the checked-in client-smoke Jest validation through PerpsController:* actions, and the package network capability is not used by the checked-in recipe.

Comment thread.yarnrc.yml Outdated

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6c7cd07. Configure here.

Comment thread.gitignore Outdated

@geosittageositta left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding a package native PerpsController client smoke test and a Farmslot recipe around it. The test itself passes, the recipe schema validates, formatting passes, and git diff --check origin/main...HEAD passes.

I am requesting changes because the new headless recipe currently depends on Jest's default Watchman behavior. In a headless agent workspace, the documented recipe command fails before producing the JSON evidence artifact.

Please add --watchman=false to the recipe’s Jest command so the validation recipe runs deterministically in the environment it is designed for.

Comment threadpackages/perps-controller/recipes/client-smoke.recipe.json Outdated
abretonc7sand others added 4 commits June 6, 2026 13:24
…tion
Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts:
#	.gitignore
#	packages/perps-controller/package.json
#	yarn.lock
Add --watchman=false to the client-smoke recipe Jest command so headless
Farmslot validation does not fail when Watchman is unavailable.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Farmslot artifact paths out of the root .gitignore into a
package-local ignore file and use package-local recipe artifact dirs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Route client-smoke recipe output and Farmslot evidence under
temp/perps-controller/ and ignore temp/ at the repo root instead of
maintaining package-local artifact directories.
Co-authored-by: Cursor <cursoragent@cursor.com>
@abretonc7s

Copy link
Copy Markdown
ContributorAuthor

Closing — superseded by a different design. Recipe/e2e validation for @metamask/perps-controller will be driven externally by the MetaMask recipe runner (the same way Mobile and Extension are driven), so @metamask/core carries no @farmslot/* dependency at all. The real HyperLiquid testnet e2e (order placement, client-bug reproduction) belongs in the runner's headless adapter layer, not as a controller-package dev dependency, and the mocked client-smoke recipe isn't needed standalone.

auto-merge was automatically disabled June 11, 2026 01:25

Pull request was closed

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@abretonc7s@geositta