fix(perps): [Bug]: Perps - Error when selecting imported account ApiRequestError: Multi-sig required - #9769
Merged
Conversation
abretonc7s
temporarily deployed
to
default-branch
August 3, 2026 18:49 — with
GitHub Actions
Inactive
Opening the Perps tab with a HyperLiquid multi-sig account raised
`ApiRequestError: Multi-sig required` on every entry.
`#ensureUnifiedAccountEnabled` migrates legacy abstraction modes with a
single-signer `agentSetAbstraction` / `userSetAbstraction` write, which
HyperLiquid rejects outright for a converted multi-sig account. Nothing
classified the account shape before the write or the rejection after it,
so it fell through to the generic failure handler: no cache entry, retry
flag set, error reported.
Query `userToMultiSigSigners` immediately before the migration write and
skip it for a multi-sig account, recording the setup as not applicable
and caching `{ attempted: true, enabled: false }` so it is not retried.
The lookup only runs when a write would otherwise be made, so accounts
already on a compatible mode and deferred accounts pay no extra round
trip, and it fails open so a transient info-API error cannot block
migration for a single-signer account.
Add `isHyperLiquidMultiSigRequiredError` and apply it in the same
method's catch block, covering the conversion racing the write and the
failed-open probe. Both `multi-sig required` and `multisig required`
spellings are matched.
Fixes TAT-3214
The Unreleased entry documented the classifier as exported from `@metamask/perps-controller/utils`, but the package exports map declares `./utils/*` with no bare `./utils`, so that specifier fails to resolve with ERR_PACKAGE_PATH_NOT_EXPORTED. Point it at `utils/*`, matching the neighbouring floorToSizeDecimals entry. Refs TAT-3214
Both the changelog entry and the isHyperLiquidMultiSigRequiredError JSDoc asserted that HyperLiquid returns the unhyphenated "multisig required" spelling. Only the hyphenated "Multi-sig required" has been observed, and the mapping table added in #9750 carries that key alone. Matching both spellings is unchanged and still correct; only the stated justification was wrong. Documentation only, no behaviour change. Refs TAT-3214
abretonc7s
force-pushed
the
TAT-3214-fix-bug-perps-error-when-selecting
branch
from
August 3, 2026 20:27
c70c277 to
72c372b
Compare
geositta
approved these changes
Aug 3, 2026
ApiRequestError: Multi-sig required
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explanation
Opening the Perps tab with a HyperLiquid multi-sig account raised
ApiRequestError: Multi-sig required, on every entry.Perps initialization runs
HyperLiquidProvider.#ensureUnifiedAccountEnabled,which migrates an account on a legacy abstraction mode to
unifiedAccount. Fordefault/disabledaccounts that is a silent agent-signedagentSetAbstractionwrite. HyperLiquid rejects every single-signerexchange write for an account that has been converted to multi-sig, so this
write could never succeed — but nothing classified the account shape before the
write, and nothing classified the rejection after it. It fell through to the
generic failure handler, which caches nothing, sets the retry flag (so
#ensureReadydrops its memoized promise), and reports the error. The resultwas a permanent error for these accounts and recurring Sentry noise.
Two changes:
HyperLiquidProvidernow readsinfoClient.userToMultiSigSignersimmediatelybefore the migration write. For a multi-sig account it skips the write, emits
Perp Account Setupwithstatus: not_applicable/error_message: multi_sig_account, and records{ attempted: true, enabled: false }inTradingReadinessCache— a finalstate, so the attempt is not repeated. The lookup only runs when a migration
write would otherwise be made, so accounts already on
unifiedAccount/portfolioMargin, deferreddexAbstractionaccounts (including hardwarewallets at init) and unknown modes pay no extra round trip. The probe fails
open on a transient info-API error, so it can never block migration for a
normal single-signer account.
isHyperLiquidMultiSigRequiredErrorclassifier (exported from@metamask/perps-controller/utils/*) is applied in the same method'scatch,covering the case where the account is converted between the lookup and the
write, and the case where the probe failed open. It matches case-insensitively
and accepts both
multi-sig requiredandmultisig required; only thehyphenated form has been observed in the wild (the ticket, and the mapping
added in fix(perps): harden HyperLiquid cancel readiness and map account-mode errors #9750), so the unhyphenated variant is defensive rather than
documented venue behaviour.
Unified account mode stays off for these accounts by design; HIP-3 collateral
continues through the existing programmatic transfer fallback. Behaviour for
single-signer accounts is unchanged apart from the one pre-write lookup.
Note for a follow-up (out of scope here): the other user-scoped exchange writes
reached from
#ensureReadyForTrading—approveBuilderFeeandsetReferrer—will hit the same rejection at trade time for these accounts, and
#setReferralCodestill routes it to the error logger.How this was verified
Reproduced and fixed against a real HyperLiquid account. A testnet account
was transiently converted to multi-sig and the real
PerpsControllerwas driventhrough
#ensureReady→#ensureUnifiedAccountEnabledfrom two isolatedcheckouts. Only the checked-out source differed:
Multi-sig requiredloggedUnified Account setup failedmainBaseline logged
Unified Account setup failed {…,"error":"Multi-sig required"}and forwarded it to the error logger — the reported bug. This branch logged
Multi-sig account, skipping unified account migration {…,"mode":"default"}andraised no error. The account was restored afterwards (signer set
null,abstraction
default, funds returned) and verified against its pre-mutationsnapshot. Testnet only; no order was placed, cancelled or edited.
Unit regression net (independently diff-sensitive). Reverting only
HyperLiquidProvider.tsanderrorUtils.ts, leaving the tests in place, failsexactly three assertions of 60 — the migration skip, the readiness-cache state,
and
logger.errorreceiving 1 call where 0 is expected — and passes again whenrestored. The classifier target fails separately with
isHyperLiquidMultiSigRequiredError is not a function.Live mainnet read path (
getAccountState/getPositions, read-only) wasalso exercised to confirm no regression to controller initialization and reads.
That tier is explicitly not diff-sensitive for this change and is not offered
as evidence for the fix.
Remaining limitation: the multi-sig runs are captured artifacts rather than a
replayable recipe step, because restoring the account to single-sig is the
required end state. A repeatable version needs a permanently multi-sig testnet
fixture account and a read-only ensure-ready action in the harness.
Relationship to #9750
#9750 (TAT-3633, now merged) maps
the same HyperLiquid rejection to a typed
EXCHANGE_MULTI_SIG_REQUIREDcode onthe order and cancel paths. This PR stops the account-setup write from being
attempted at all, which is what produces the error on Perps tab entry. The two
are complementary and neither subsumes the other.
HyperLiquidProvider.tsauto-merges cleanly — that PR touches ~511, 2393,3439–3463, 3711, 3737, 4061–4096, this one 136, 785, 980, 1075 — and the test
files are disjoint.
packages/perps-controller/CHANGELOG.mddoes conflictnow that #9750 has merged: both add entries to the same Unreleased sections.
git merge-tree --write-treeagainst currentmainreportsCONFLICT (content)on that file alone. It needs a trivial resolution (keepboth sets of entries) before merge.
Optional follow-up for whichever merges second:
#mapError's static table keysonly on
'multi-sig required', while the classifier added here also matches theunhyphenated spelling.
#mapErroralready callsisHyperLiquidUserNotFoundErroras a function for the analogous
EXCHANGE_ACCOUNT_NOT_FOUNDcase, soisHyperLiquidMultiSigRequiredErrorcould be reused there instead of keeping twomatchers. Not a blocker for either PR.
The blocking review comment on #9750 about a dropped cancel status error is a
separate
cancelOrderresponse-shape issue and does not affect this change.References
ApiRequestError: Multi-sig requiredmetamask-mobile#34170, whichdemonstrated the behaviour but belongs in
@metamask/perps-controller. NoMobile dependency bump is included here; Mobile picks this up through a normal
release.
rejection on the order/cancel paths. Complementary surfaces; this branch
auto-merges against
mainwith no textual conflicts.Checklist
Screenshots/Recordings
LIVE diff-sensitive Core proof for TAT-3214. A HyperLiquid TESTNET account (dev3) was transiently converted to multi-sig and the real PerpsController was driven through #ensureReady -> #ensureUnifiedAccountEnabled from two isolated checkouts: origin/main logged the genuine 'Multi-sig required' rejection, this branch skipped the write. The account was restored and verified against its pre-mutation snapshot (signers null, mode default, funds returned). Supporting tiers: live mainnet read-only non-regression, and a re-runnable unit regression net. No mainnet mutation, no trading, no visual evidence.