> For the complete documentation index, see [llms.txt](https://docs.trover.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trover.tech/engineering/verify-2026-08-31-price-decimals.md).

# Verify: raw payment units were being read as whole units

Commit: `a3690be` — `fix(nft): stop reading raw payment units as whole ones`

## Migration this round?

**No migration.** Nothing in `packages/db` changed. Do not run the `migrate` service for this deploy.

## What was wrong

`normalizedAmount` in `packages/domain/src/nft-market.ts` divided by `10^decimals` **only when the raw integer was already big enough**:

```ts
&& parsed >= 10 ** Math.min(decimals, 12)   // the bug
```

At 6 decimals that means `1000000 -> 1` (correct) but `500000 -> 500000` (truth: `0.5`). **Every payment under 1.0 whole units of its token was returned as its raw integer.** A 0.5 USDG sale became 500,000 USDG, then $500,000, then \~203 ETH on the chart.

This was never specific to `proofmachines`. It hit any sale below one whole unit of any token carrying `decimals` — every stablecoin-denominated collection on the chain. ETH escaped by luck: 1e15 wei clears the 1e12 threshold. A 0.0000001 ETH sale would have broken the same way.

Evidence it is per-sale rather than a global scale error — the 12:25 candle on production today opens at `0.000407` and highs at `358.42`, i.e. a correctly-parsed and a misparsed sale inside the same minute.

## The fix

When `decimals` is present and valid, **always** divide. The `> 1e9 ? /1e18 : value` heuristic is kept only for payloads with no `decimals` field.

Plus `repairMispricedEvents()` in `apps/worker/src/nft-market-indexer.ts`: on each collection sync it re-normalizes the 1,000 newest accepted sale/mint rows against their own stored `providerPayload`, skips anything already within float noise, and counts repriced rows toward the candle rebuild so `rebuildAllCandles` reruns. It is idempotent — a second pass changes nothing.

Verified before push with a 9-case truth table (all correct) and confirmed `normalizeStatsFloor` does **not** route through `normalizedAmount`, so floors are untouched by this change.

## Deploy

Services changed: **api + worker** (`packages/domain` is compiled into both). Web is unaffected; nothing to do on Vercel.

```bash
cd ~/trover
git pull --ff-only origin main
docker compose -f docker-compose.yml -f docker-compose.production.yml build api worker
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --force-recreate api worker
```

## Verify after deploy

The repair pass runs on the next collection sync, so give the worker a couple of minutes (or hit the resync route) before checking.

```bash
curl -s "https://api.trover.tech/v1/nft-market/collections/proofmachines/candles?chain=robinhood&interval=1m" \
  | python3 -c 'import json,sys; [print(c["time"], c["close"]) for c in json.load(sys.stdin)["candles"]]'
```

**Before** (what production serves right now):

```
2026-08-31T12:20:00.000Z 203.2718639216834
2026-08-31T12:25:00.000Z 358.4200193872647
2026-08-31T12:33:00.000Z 203.68838935443
2026-08-31T12:49:00.000Z 204.3269918408146
2026-08-31T12:53:00.000Z 204.2283436264419
```

**After**: every close should land around `0.0002`–`0.0005`, in the same range as that 12:25 open of `0.000407` which was already correct.

Cross-check one of them against the transaction on `rh-scan.com` to confirm the absolute value, not just that it got smaller.

Then spot-check a collection that trades in ETH (not a stablecoin) and confirm its candles did **not** move — ETH sales were already correct and must stay correct.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.trover.tech/engineering/verify-2026-08-31-price-decimals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
