# Umsetzungsplan: On-Chain-Auto-Redeem (modulweise schaltbar)

> Stand: 2026-07-11
> Ziel: Gewonnene Positionen automatisch on-chain einlösen (Shares → USDC), als
> **Core-Baustein** mit **Aktivierung je Modul**. Richards Anforderung: In Testphasen
> neuer Module soll Auto-Redeem gezielt AUS bleiben können, um die Performance der
> Entwicklung manuell nachvollziehen zu können — ohne dass andere, etablierte Module
> ihren Automatismus verlieren.
> ⚠️ Höchste clob.md-Kritikalität: On-Chain-Signing mit echten Private Keys.
> Jede Phase: Backup/Commit vorher, Testmarkt/Kleinstbetrag zuerst.

---

## 1. Architektur: Queue statt Direktaufruf

Module redeemen nie selbst. Sie melden einlösbare Positionen in eine zentrale
Queue; ein Core-Worker arbeitet sie ab — nur für Module, deren Auto-Redeem aktiv ist.

```
Modul (RF-Monitor, CT-Sync, später DD)                PolyTrader.Core
  │  erkennt „Position gewonnen & aufgelöst"            │
  ├── IRedeemQueue.Enqueue(RedeemRequest) ─────────────▶│  Tabelle core_redeem_queue
  │      (immer! unabhängig vom Schalter)               │
  │                                                     ▼
  │                                    OnChainRedeemWorker (BackgroundService)
  │                                      • nur Requests von Modulen mit AutoRedeemEnabled
  │                                      • Rest bleibt als "Manual" sichtbar (UI-Liste)
  ▼                                      • CTF redeemPositions / NegRiskAdapter
UI je Modul: Pending-Redeems-Ansicht     • Verifikation über USDC-Balance-Delta
```

**Warum immer enqueuen:** Auch bei deaktiviertem Auto-Redeem entsteht so eine
vollständige, je Modul gefilterte „zum Redeem bereit"-Liste (Dashboard/UI) — genau
die Übersicht, die Richard in Testphasen für die manuelle Betreuung will. Der
Schalter entscheidet nur, ob der Worker sie abarbeitet.

### 1.1 Datenmodell (`core_redeem_queue`)

| Feld | Inhalt |
|---|---|
| Id (auto) | PK |
| ModuleName | "ResolutionFarming" / "CopyTrading" / … |
| AccountId, TokenId, ConditionId | Ziel der Einlösung (ConditionId zwingend — für den Contract-Call) |
| IsNegRisk | Adapter-Wahl |
| SizeShares, ExpectedUsd | erwartete Auszahlung (Shares × 1.00) |
| Status | Pending / Processing / Done / Failed / Manual |
| Attempts, LastError, EnqueuedAt, CompletedAt, TxHash | Betrieb/Nachvollziehbarkeit |

### 1.2 Schalter

- **Je Modul:** `core_module_settings` (oder appsettings-Sektion) —
  `AutoRedeem:ResolutionFarming = false`, `AutoRedeem:CopyTrading = false`
  (Default IMMER aus; bewusstes Einschalten je Modul).
- **Global-Not-Aus:** `AutoRedeemGlobalEnabled` (analog GlobalTradingPaused,
  Quickbar-Toggle) — schlägt alle Modul-Schalter.
- UI: Schalter je Modul in dessen Settings-Tab + Anzeige im Core-Dashboard,
  welche Module aktiv sind.

## 2. Der On-Chain-Teil (`OnChainCtfService`, Core)

1. **Bibliothek:** Nethereum (bereits als Abhängigkeit im Projekt für EIP-712-Signing
   vorhanden) — Contract-Calls über die bestehende Alchemy-RPC-Anbindung (Polygon).
2. **Aufrufe:** Standard-Markt: ConditionalTokens `redeemPositions(collateral,
   parentCollectionId=0x0, conditionId, indexSets)`; NegRisk-Markt: über den
   NegRisk-Adapter. **Contract-Adressen + ABI + indexSets-Ermittlung bei Umsetzung
   zwingend aus https://docs.polymarket.com (Developer/CTF) verifizieren — nicht aus
   dem Gedächtnis kodieren.** Die USDC-/CTF-Adressen als Konstanten mit Quellenangabe.
3. **Gas:** Wallet braucht POL. Vor jedem Call Balance-Check; unter Schwelle
   (Setting, z. B. 0.5 POL) → Request auf `Manual` + Threema-Warnung „POL nachfüllen".
   Gas-Preis: Standard-Estimation, Cap als Setting.
4. **Verifikation = Wahrheit:** Ein Redeem gilt erst als `Done`, wenn (a) die Tx
   bestätigt ist UND (b) das USDC-Balance-Delta ≈ ExpectedUsd (Toleranz) gemessen
   wurde. Sonst `Failed` mit Fehlertext.
5. **Retry:** max. 3 Versuche mit Backoff (1/10/60 min), danach `Manual` + Threema.
   Idempotenz beachten: vor jedem Versuch prüfen, ob die Position on-chain überhaupt
   noch einlösbar ist (bereits redeemte Shares → als Done werten, nicht als Fehler).

## 3. Modul-Integration

### 3.1 ResolutionFarming (erster Nutzer)
`FarmingResolutionMonitorService` setzt heute `RedeemStatus = "Pending"` am
RfClosedTrade. Ergänzung: beim Schließen eines Gewinners → `IRedeemQueue.Enqueue`.
Worker-Callback (oder Status-Poll) aktualisiert `RedeemStatus` (Pending → Redeemed/
Manual/Failed) → sichtbar im Historie-Tab. **Wichtig für Richards Testphasen-
Anforderung:** Der realisierte PnL ist bereits bei Resolution gebucht; der Redeem
ändert nur die Kapitalverfügbarkeit. Die Performance-Auswertung bleibt also mit und
ohne Auto-Redeem identisch — nur die Bankroll-Rotation unterscheidet sich.

### 3.2 CopyTrading (zweiter Nutzer)
Im `TraderMonitorService` existiert die Stelle bereits: der auskommentierte
Python-Redeem-Block im Resolution-Fallback („bereit für manuellen Redeem")
— dort `Enqueue` statt Kommentar. Gleicher PnL-Hinweis wie oben.

### 3.3 Künftige Module
Contract: Ein Modul, das Positionen bis Resolution hält, ruft bei „gewonnen &
aufgelöst" genau einmal `Enqueue` und liest optional den Status zurück. Mehr nicht.

## 4. Phasen & Akzeptanzkriterien

| Phase | Inhalt | Akzeptanz |
|---|---|---|
| RD-1 | Queue-Tabelle + IRedeemQueue + Modul-Schalter + UI-Pending-Liste (noch KEIN On-Chain-Code) | Module enqueuen; Liste zeigt je Modul „bereit zum Redeem"; Schalter sichtbar; 100 % ohne Live-API testbar |
| RD-2 | `OnChainCtfService` gegen Polygon: zuerst READ-only (Balance, Einlösbarkeits-Check) | Balance-/Zustandsabfragen stimmen gegen Polygonscan |
| RD-3 | Erster echter Redeem: EIN Testmarkt, Kleinstbetrag, manuell getriggert (Button an der Pending-Liste) | Tx bestätigt, USDC-Delta verifiziert, Status Done |
| RD-4 | Worker-Automatik scharf für RF (Schalter an), CT folgt nach Beobachtung | 1 Woche fehlerfreier Betrieb, Failed-Quote < 5 %, POL-Warnung getestet |

## 5. Leitplanken

1. `.agents/rules/clob.md` gilt verschärft: Private-Key-Nutzung außerhalb des
   erprobten Order-Signing-Pfads. Jede Phase einzeln committen; RD-3 nie
   überspringen.
2. Der Worker fasst NUR Queue-Einträge an — er scannt nie selbst Positionen
   (klare Verantwortung: Module erkennen, Core löst ein).
3. Alle Beträge/TxHashes loggen; Threema-Tageszusammenfassung „X Redeems, Y USDC
   freigesetzt, Z manuell offen".
4. Manuelle Redeems (über die Website) müssen erkannt werden: Einlösbarkeits-Check
   in 2.5 markiert extern eingelöste Einträge als Done statt Failed.
