celo-org / celo-org/mondeto

A rate limit whose viem args carry a nonce is classified as `nonce`, not `rpc`

Open
#261 0 comments 0 reactions 1 assignee Claimed by @GigaHierz View on GitHub
bug priority:low size:S
Dominant language
TypeScript
Stars
1
Forks
2
Avg merge
6d 8h
Merged PRs (30d)
10

Description

## What happens

A rate-limited buy whose formatted viem `Request Arguments` contain a nonce is classified as
`nonce`, not `rpc`, and the player is told:

> Nonce error — please try again in a few seconds

for what is actually a rate limit. The advice is wrong (waiting a few seconds does not clear a rate
limit any faster than it clears itself) and the analytics category is wrong, which matters because
`rpc` volume is the signal we use to decide whether Forno needs replacing.

## Root cause

`RULES` in `apps/web/src/lib/buyErrors.ts` is first-match-wins, and `nonce` is **rule 1** while `rpc`
is **rule 9**. Rule 1's test is a bare substring check:

```ts
{
category: 'nonce',
test: (hay) => hay.includes('nonce'),
...
}
```

viem includes a formatted `Request Arguments:` block in the message of many transport errors, and that
block contains a `nonce:` line whenever the request carried one. So a 429 on a request that had a
nonce matches rule 1 before rule 9 can see it.

## Reachability — NOT confirmed, and that is the point of this issue

Raised during review of #246 and deliberately recorded as **reachability-unverified**:

- The mechanism is real and reproducible — there is a constructed fixture in
`apps/web/src/__tests__/lib/buyErrors.test.ts` that demonstrates it, and it is pinned there as
known behaviour so a deliberate reorder shows up as a test change.
- **No real captured viem error has been produced** showing the nonce token in a rate-limited buy.
The fixture is hand-built. Whether Forno's 429 responses on this app's buy path actually carry a
nonce in the formatted args is unknown.

This is filed so the finding does not evaporate, **not** as a confirmed defect. Treat the first task
as answering the reachability question, not as writing a fix.

## Suggested fix — only if reachable

Tighten rule 1 so it matches a nonce *error* rather than the presence of the word. Something like
requiring `nonce too low` / `nonce too high` / `invalid nonce` / `nonce has already been used`, rather
than a bare `includes('nonce')`.

**Do not simply move `rpc` above `nonce`.** The rule ordering is load-bearing in both directions and
the file says so; reordering to fix this could hand a genuine nonce error to `rpc`. The ordering guard
added in #246 will go red if anyone tries, which is the intended behaviour.

**What this does NOT fix:** nothing about the player-facing copy for genuine nonce errors, and nothing
about `rpc` classification generally — #246 already widened the haystack that fed this path.

## Non-goals

No second classifier. No change to `GENERIC_RETRY_MESSAGE` behaviour for other categories.

## Depends on / blocks

Depends on #246 (which pins the current behaviour). Blocks nothing. **Explicitly not a blocker** for
#246 or #251.

## Acceptance criteria

- [ ] Reachability answered first: either a captured real viem error showing the nonce token in a
rate-limited buy, or a written finding that it does not occur and why — recorded here either way
- [ ] If reachable: rule 1 matches nonce *errors*, not the substring — pinned in a test
- [ ] If reachable: the rate-limit-with-nonce fixture in `buyErrors.test.ts` moves from the
known-migration list to asserting `rpc`
- [ ] If not reachable: the pinned fixture keeps its comment, updated to say so with the evidence
- [ ] Rule ordering guard from #246 still green either way
- [ ] Mutation count stated in the PR

## How we'd know it's fixed

The `nonce` category stops appearing in analytics alongside 429s. Today that correlation cannot be
checked because both collapse into the same bucket — which is itself part of the reason to answer the
reachability question with captured data rather than reasoning.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.