# 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.