> 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-wallet-send-and-health.md).

# Verify: wallet fan-out send, 1m chart default, stat/health repair

## Deploy

**No migration this round.** api + worker changed; web ships via Vercel on push.

```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
```

## 1. Wallet manager send

Header wallet dropdown → each wallet row has a send (paper-plane) button. Clicking it turns that row into an amount field (Ξ) with send/cancel, and puts a checkbox on every row so you can pick recipients. The sender's own checkbox is disabled.

* **Delegated wallet**: submits with no second prompt.
* **Any other wallet**: a signing tab opens for the first recipient; the rest appear in the sent list with their own `sign` buttons.

Nothing new signs or submits transactions: the route creates ordinary `TransferIntent` rows and hands them to the existing worker pipeline, which already forks on wallet capability (server-side submit for delegated, signing handoff otherwise) and already enforces the balance, gas reserve, transaction simulation, confirmation setting and `TRADING_DRY_RUN`.

```bash
# Should 401 without a session; with one it returns 202 and a transfers array.
curl -i -X POST "https://api.trover.tech/v1/trading/wallet-transfers" \
  -H 'content-type: application/json' \
  -d '{"fromWalletId":"<id>","recipients":["0x…"],"amount":"0.001"}'
```

Worth checking once on a real send: `TRADING_DRY_RUN` is **off** in production, and the profile's `requireTradeConfirmation` — if it is on, transfers land in `confirmation_required` rather than sending, and the row will say so.

## 2. Chart opens on 1m

`/nfts/robinhood/<slug>` — the interval selector starts on `1m` and the range on `24h` (1m candles across 30 days would be 43,200 buckets). A previously stored preference still wins, so clear `trover:nft-chart-interval` in localStorage to see the new default.

## 3. Stats showing zeros, and permanent "degraded"

Root cause: **OpenSea returns 0 for every stat of a collection it has not finished computing**, which on Robinhood chain is most of them, and we stored those zeros as facts. That is the "0 ETH ($0) floor" and "0 owners". Verified live — e.g. `ruggged-hunters` reports 712 sales in 24h alongside volume 0 and floor 0.

Now: a zero floor is treated as unknown (renders "—", not "0 ETH"); volume 0 alongside a nonzero sale count is treated as unknown; owner count prefers our own on-chain ownership walk; and a missing ETH/USD rate no longer overwrites good USD figures with nulls.

Separately, a collection whose sync failed used to stay `syncStatus: "failed"` forever, because only watched collections are ever re-synced and watch leases expire. `rhmachines` and `hopium-machines` had been stuck since **2026-08-29** — two days — while their data quietly kept flowing. Failed syncs are now always retried with 1→30 min backoff, and the terminal's status badge became a button that queues a repair immediately and names what is actually down.

```bash
# Before/after on the collections that were showing zeros:
for s in ruggged-hunters satoshi-bot hood-homies-237429686; do
  curl -s "https://api.trover.tech/v1/nft-market/collections/$s?chain=robinhood" \
  | python3 -c 'import json,sys; m=json.load(sys.stdin)["market"]; print(m["slug"], m["floor"], m["owners"], m["sync"]["status"])'
done
#  expect: floor native null (not 0) where OpenSea has no stats, owners from
#  our own walk where the chain has been indexed, and sync status leaving
#  "failed" within a few minutes.

# Manual repair (what the badge calls):
curl -i -X POST "https://api.trover.tech/v1/nft-market/collections/rhmachines/resync?chain=robinhood"

# Confirm the auto-retry is running:
docker compose logs --tail=300 worker | grep "Retrying failed NFT collection sync"
```

### Still needs your side

`health.openSeaConnected` is `false` for **every** collection, and a brand-new one reports `opensea_stream: connecting`. That is the OpenSea Stream connection itself, not per-collection state, so it is an env/credential question (`OPENSEA_STREAM_ENABLED`, and whether the API key has stream access) rather than something the code can retry its way out of. Check:

```bash
docker compose logs --tail=300 worker | grep -i "stream"
```


---

# 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-wallet-send-and-health.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.
