Contracts: Manual DOUB podium pool top-up (no platform take)
## Context
Arena v2 buy routing ([GitLab #249](https://gitlab.com/PlasticDigits/yieldomega/-/work_items/249)) sends **70%** of gross DOUB to prizes (**10%** → each active podium pool, **7.5%** → each next-round seed pool) and **30%** to `AdminSellVault`. Operators and participants also need a **permissionless manual top-up** path that boosts prize liquidity **without** the platform take.
Parent epic: [#238](https://gitlab.com/PlasticDigits/yieldomega/-/work_items/238). Complements [#249](https://gitlab.com/PlasticDigits/yieldomega/-/work_items/249) (shared `PodiumVaults` / routing helpers).
## Relevant files
- `contracts/src/TimeArena.sol` — new external entrypoint
- `contracts/src/PodiumVaults.sol` (or prize-routing module from #249)
- `contracts/src/Doubloon.sol` — `transferFrom` / allowance
- `docs/product/time-arena.md` (spec [#240](https://gitlab.com/PlasticDigits/yieldomega/-/work_items/240)) — document donor economics
- `indexer/src/decoder.rs` — ingest new event(s)
- Optional later: protocol / arena UI donate CTA (not blocking for this issue)
## Behavior
Add a **public** function (e.g. `topUpPodiumPools(uint256 amountDoubWad)`) that:
1. **`require(amountDoubWad > 0)`**
2. Pulls DOUB from **`msg.sender`** via **`IERC20.transferFrom(msg.sender, …, amountDoubWad)`** (balance-delta parity per [#123](https://gitlab.com/PlasticDigits/yieldomega/-/work_items/123))
3. Allocates **100%** of `amountDoubWad` across the **eight** prize vaults using the **same per-category ratios as buys**, but **no** `AdminSellVault` slice:
| Destination (per podium category) | Share of top-up amount |
|-----------------------------------|------------------------|
| Active podium pool | **10 / 70** (≈ 14.29%) |
| Next-round seed pool | **7.5 / 70** (≈ 10.71%) |
Applied independently for **Last Buy**, **Streak**, **Time Booster**, and **WarBow** (four categories × two vaults). Integer rounding: document remainder policy (recommend: last vault or largest active pool — match #249).
**Equivalence:** A manual top-up of **700 DOUB** must land the same per-vault increments as the **prize portion** of a **1000 DOUB buy** (100 active + 75 seed per category).
4. Emits indexer-friendly events (e.g. `PodiumPoolsToppedUp(donor, amountDoubWad)` plus per-vault `PodiumFunded` / `SeedFunded` if reusing #249 events)
5. **No** minting of CHARM, CRED, or XP; **no** timer extension; **no** `totalRaised` increment unless product explicitly wants gross accounting (default: **do not** count toward buy stats)
## Acceptance criteria
- [ ] `topUpPodiumPools(amount)` pulls DOUB only from `msg.sender` (`transferFrom`); reverts without allowance/balance
- [ ] **Zero** DOUB from this path goes to `AdminSellVault` (no 30% platform take)
- [ ] Per-category split matches buy prize ratios (**10% : 7.5%** active:seed), normalized over **100%** of donated amount
- [ ] Reuses shared internal routing with buy path where possible (single source of truth for bps)
- [ ] Distinct event(s) emitted for indexer / wallet stats (donor address + amount)
- [ ] `nonReentrant` if sharing vault code with `buy`
- [ ] Spec note in `docs/product/time-arena.md`: manual top-up is voluntary prize sponsorship only
## Verification checklist
- [ ] Forge: approve + `topUpPodiumPools(700e18)` → each active pool +100e18, each seed +75e18 (four categories)
- [ ] Forge: `topUpPodiumPools(1000e18)` → `AdminSellVault` balance unchanged
- [ ] Forge: fuzz amounts; sum of vault deltas == donated amount (no dust loss beyond documented remainder rule)
- [ ] Forge: `transferFrom` failure reverts with no vault mutation
- [ ] Indexer: migration + decode row for new event (can be follow-up linked issue if preferred)
issue