Commit 66b4d06
authored
fix(perps): suppress "User or API Wallet does not exist" Sentry noise from unfunded wallets (#29972)
## **Description**
Hyperliquid creates user accounts server-side on first USDC deposit.
Until then, every user-scoped exchange write (`agentSetAbstraction`,
`userSetAbstraction`, `setReferrer`) rejects with `User or API Wallet
0x... does not exist.` and the catch in
`HyperLiquidProvider.#ensureUnifiedAccountEnabled` was forwarding these
benign rejections to Sentry on every Perps section open.
Last 14d on `7.75.1+4800`:
-
[METAMASK-MOBILE-4XB5](https://metamask.sentry.io/issues/METAMASK-MOBILE-4XB5)
(iOS) — 316k events / 63k users
-
[METAMASK-MOBILE-4Q4M](https://metamask.sentry.io/issues/METAMASK-MOBILE-4Q4M)
(Android) — 1.7M events / 176k users
Source pinned by event Additional Context: `HyperLiquidProvider.method:
ensureUnifiedAccountEnabled`.
**Fix: proactive gate.** Probe `infoClient.userNonFundingLedgerUpdates`
once (cheap, ~100 ms, non-throwing) before any user-scoped exchange
write. The ledger is empty if and only if the wallet has never
interacted with Hyperliquid — a necessary and sufficient precondition
for the exchange writes to succeed. When empty, skip the migration /
referral writes, fire `Perp Account Setup` with `status:
not_applicable`, `error_message: no_hl_account`. Positive observations
cached in `PerpsSigningCache.walletRegistered`; negative results
re-probe on next entry so the gate self-heals once the user deposits.
`isHyperLiquidUserNotFoundError` remains as a safety net in three
catches (`ensureUnifiedAccountEnabled`, `ensureReferralSet`,
`setReferralCode`) for the small race window where the probe succeeded
but the write still rejected. Unrelated SDK errors keep reaching
`logger.error`.
### Reproduced standalone
Confirmed with a node script (not committed) that drives the SDK
directly with a freshly-generated EOA. Key signals:
- `exchangeClient.agentSetAbstraction({abstraction:'u'})` rejects with
the exact Sentry string.
- `infoClient.userAbstraction` is **not** the throw site — it returns
`'default'` for fresh wallets.
- `infoClient.userNonFundingLedgerUpdates` returns `[]` for fresh
wallets — a clean array-length discriminator (no string parsing).
### Why this supersedes #29828
#29828 catches and classifies after the SDK rejects. This PR prevents
the SDK call when we already know it will fail.
| | #29828 | This PR |
|---|---|---|
| Exchange round-trip for unfunded wallets | Full call | Skipped |
| HL-side error logs | Generated | None |
| Coverage | `ensureUnifiedAccountEnabled` only |
`ensureUnifiedAccountEnabled` + `ensureReferralSet` |
| New throw sites | Need a new catch | Existing probe gates them |
Cherry-picked from #29828: `isHyperLiquidUserNotFoundError` helper,
`reason: 'no_hl_account'` discriminator, `STATUS.NOT_APPLICABLE`
constant.
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Supersedes: #29828
Sentry: METAMASK-MOBILE-4XB5, METAMASK-MOBILE-4Q4M, METAMASK-MOBILE-4KC4
## **Manual testing steps**
```gherkin
Feature: Unfunded wallets do not pollute Sentry with HL exchange-write rejections
Scenario: Fresh wallet opens Perps without depositing
Given the wallet has never deposited to Hyperliquid
When the user opens the Perps section
Then no Sentry event is captured with title "ApiRequestError: User or API Wallet ** does not exist."
And one Segment "Perp Account Setup" event with status=not_applicable, error_message=no_hl_account is fired
Scenario: Wallet funds during the session
Given the gate previously deferred for this wallet
When the user deposits USDC and re-enters Perps
Then the unified account migration runs as before
Scenario: Unrelated SDK error still surfaces
Given the wallet is funded
When agentSetAbstraction throws e.g. "Insufficient margin"
Then logger.error fires as before
And the Segment event has status=failed (not not_applicable)
```
Automated:
```
yarn jest \
app/controllers/perps/providers/HyperLiquidProvider.test.ts \
app/controllers/perps/services/TradingReadinessCache.test.ts \
app/controllers/perps/utils/errorUtils.test.ts \
--no-coverage
# 3 suites, 395 passed, 0 failed
```
`yarn lint` clean. `tsc --noEmit` clean.
## **Screenshots/Recordings**
N/A — internal observability change. Verify via Sentry dashboard delta
24–48h after release.
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using JSDoc where applicable
- [x] I've applied the right labels on the PR
#### Performance checks (if applicable)
- [x] I've tested on Android — N/A (no runtime UX change; one cheap
extra read on Perps init for unfunded wallets)
- [x] I've tested with a power user scenario — N/A
- [x] I've instrumented key operations with Sentry traces for production
performance metrics — N/A; removes noise rather than adds traces
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR
- [ ] I confirm that this PR addresses all acceptance criteria
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches Hyperliquid account setup and referral flows and adds new
caching/gating logic; mistakes could defer migration/referral for some
funded users, though the probe is designed to fail open and has test
coverage.
>
> **Overview**
> Reduces Perps Sentry noise by **proactively skipping Hyperliquid
exchange writes** when the wallet has no Hyperliquid account yet (no
funding/ledger history), and instead tracking `Perp Account Setup` with
`status: not_applicable` and `error_message: no_hl_account`.
>
> Adds `#isWalletOnHyperliquid` (ledger probe + positive-only cache in
`PerpsSigningCache.walletRegistered`) and uses it to gate
`#ensureUnifiedAccountEnabled` and referral setup; also introduces
`isHyperLiquidUserNotFoundError` as a safety-net classifier so these
benign rejections are debug-logged rather than sent to Sentry.
>
> Extends analytics constants with `STATUS.NOT_APPLICABLE`, enriches
`TradingReadinessCache` entries with an optional `reason`, and adds
focused unit tests covering the new gate, cache behavior, and error
classification.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9e54db7. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 5b1913f commit 66b4d06
7 files changed
Lines changed: 654 additions & 5 deletions
File tree
- app/controllers/perps
- constants
- providers
- services
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
360 | 360 | | |
361 | 361 | | |
362 | 362 | | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
363 | 367 | | |
364 | 368 | | |
365 | 369 | | |
| |||
Lines changed: 206 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10838 | 10838 | | |
10839 | 10839 | | |
10840 | 10840 | | |
| 10841 | + | |
| 10842 | + | |
| 10843 | + | |
| 10844 | + | |
| 10845 | + | |
| 10846 | + | |
| 10847 | + | |
| 10848 | + | |
| 10849 | + | |
| 10850 | + | |
| 10851 | + | |
| 10852 | + | |
| 10853 | + | |
| 10854 | + | |
| 10855 | + | |
| 10856 | + | |
| 10857 | + | |
| 10858 | + | |
| 10859 | + | |
| 10860 | + | |
| 10861 | + | |
| 10862 | + | |
| 10863 | + | |
| 10864 | + | |
| 10865 | + | |
| 10866 | + | |
| 10867 | + | |
| 10868 | + | |
| 10869 | + | |
| 10870 | + | |
| 10871 | + | |
| 10872 | + | |
| 10873 | + | |
| 10874 | + | |
| 10875 | + | |
| 10876 | + | |
| 10877 | + | |
| 10878 | + | |
| 10879 | + | |
| 10880 | + | |
| 10881 | + | |
| 10882 | + | |
| 10883 | + | |
| 10884 | + | |
| 10885 | + | |
| 10886 | + | |
| 10887 | + | |
| 10888 | + | |
| 10889 | + | |
| 10890 | + | |
| 10891 | + | |
| 10892 | + | |
| 10893 | + | |
| 10894 | + | |
| 10895 | + | |
| 10896 | + | |
| 10897 | + | |
| 10898 | + | |
| 10899 | + | |
| 10900 | + | |
| 10901 | + | |
| 10902 | + | |
| 10903 | + | |
| 10904 | + | |
| 10905 | + | |
| 10906 | + | |
| 10907 | + | |
| 10908 | + | |
| 10909 | + | |
| 10910 | + | |
| 10911 | + | |
| 10912 | + | |
| 10913 | + | |
| 10914 | + | |
| 10915 | + | |
| 10916 | + | |
| 10917 | + | |
| 10918 | + | |
| 10919 | + | |
| 10920 | + | |
| 10921 | + | |
| 10922 | + | |
| 10923 | + | |
| 10924 | + | |
| 10925 | + | |
| 10926 | + | |
| 10927 | + | |
| 10928 | + | |
| 10929 | + | |
| 10930 | + | |
| 10931 | + | |
| 10932 | + | |
| 10933 | + | |
| 10934 | + | |
| 10935 | + | |
| 10936 | + | |
| 10937 | + | |
| 10938 | + | |
| 10939 | + | |
| 10940 | + | |
| 10941 | + | |
| 10942 | + | |
| 10943 | + | |
| 10944 | + | |
| 10945 | + | |
| 10946 | + | |
| 10947 | + | |
| 10948 | + | |
| 10949 | + | |
| 10950 | + | |
| 10951 | + | |
| 10952 | + | |
| 10953 | + | |
| 10954 | + | |
| 10955 | + | |
| 10956 | + | |
| 10957 | + | |
| 10958 | + | |
| 10959 | + | |
| 10960 | + | |
| 10961 | + | |
| 10962 | + | |
| 10963 | + | |
| 10964 | + | |
| 10965 | + | |
| 10966 | + | |
| 10967 | + | |
| 10968 | + | |
| 10969 | + | |
| 10970 | + | |
| 10971 | + | |
| 10972 | + | |
| 10973 | + | |
| 10974 | + | |
| 10975 | + | |
| 10976 | + | |
| 10977 | + | |
| 10978 | + | |
| 10979 | + | |
| 10980 | + | |
| 10981 | + | |
| 10982 | + | |
| 10983 | + | |
| 10984 | + | |
| 10985 | + | |
| 10986 | + | |
| 10987 | + | |
| 10988 | + | |
| 10989 | + | |
| 10990 | + | |
| 10991 | + | |
| 10992 | + | |
| 10993 | + | |
| 10994 | + | |
| 10995 | + | |
| 10996 | + | |
| 10997 | + | |
| 10998 | + | |
| 10999 | + | |
| 11000 | + | |
| 11001 | + | |
| 11002 | + | |
| 11003 | + | |
| 11004 | + | |
| 11005 | + | |
| 11006 | + | |
| 11007 | + | |
| 11008 | + | |
| 11009 | + | |
| 11010 | + | |
| 11011 | + | |
| 11012 | + | |
| 11013 | + | |
| 11014 | + | |
| 11015 | + | |
| 11016 | + | |
| 11017 | + | |
| 11018 | + | |
| 11019 | + | |
| 11020 | + | |
| 11021 | + | |
| 11022 | + | |
| 11023 | + | |
| 11024 | + | |
| 11025 | + | |
| 11026 | + | |
| 11027 | + | |
| 11028 | + | |
| 11029 | + | |
| 11030 | + | |
| 11031 | + | |
| 11032 | + | |
| 11033 | + | |
| 11034 | + | |
| 11035 | + | |
| 11036 | + | |
| 11037 | + | |
| 11038 | + | |
| 11039 | + | |
| 11040 | + | |
| 11041 | + | |
| 11042 | + | |
| 11043 | + | |
| 11044 | + | |
| 11045 | + | |
| 11046 | + | |
10841 | 11047 | | |
0 commit comments