Kontext & Ziel
Umsetzung von Phase 3 des RealUnit-App-Projekts: ein Kredit-/Lombard-Feature. Der Nutzer hinterlegt RealUnit-Token (REALU) als Sicherheit (Collateral) — vertraglich „analog WBTC" — und erhält im Gegenzug einen Stablecoin-Kredit, der via DFX-Off-Ramp aufs Bankkonto ausgezahlt wird. Die Rückzahlung gibt das Collateral wieder frei.
Der Auftraggeber hat Frankencoin (ZCHF) als Referenzmodell für „analog WBTC" benannt: ein oracle-freies, collateralized-minting-System, in dem ein beliebiges ERC-20 (heute u.a. WBTC) als Pfand dient, um ZCHF zu leihen.
⚠️ Dieses Issue startet NICHT mit Implementierung. Schritt 0 ist eine vollständige, tiefe Analyse der beteiligten Repos und der regulatorischen Rahmenbedingungen. Erst danach wird der genaue Architektur-/Implementierungsweg festgelegt (als Folge-Spec / Sub-Issues). Nicht vorher mit Code beginnen.
🔒 Architektur-Leitplanke (HART, nicht verhandelbar)
Die RealUnit-App spricht ausschliesslich mit api.dfx.swiss — niemals direkt mit Frankencoin, Ponder, einem Chain-RPC oder einem anderen Drittservice. Die DFX-API ist die alleinige Vermittlungs-/Autoritätsschicht. Sie nutzt ihrerseits api.frankencoin.com / ponder.frankencoin.com als Datenquelle oder betreibt ein eigenes passendes Backend. Die App bleibt reine Client-/Rendering-Schicht (vgl. API-as-Decision-Authority).
RealUnit-App ──► api.dfx.swiss ──► api.frankencoin.com / ponder.frankencoin.com
(nur Client) (Integrations-/ (Read: Positionen, Kurse, Status)
Autoritätsschicht) ──► Frankencoin Contracts (Write: openPosition/mint/repay)
└► oder eigenes DFX-Backend
Konsequenz: Jede Frankencoin-Interaktion (Reads wie Writes) wird hinter neuen api.dfx.swiss-Endpoints gekapselt. Die App kennt Frankencoin nicht — sie kennt nur RealUnit-/DFX-Loan-Endpoints. Tx-Signing bleibt im Wallet (non-custodial), aber der unsigned-Tx-Bau + Broadcast laufen über die DFX-API (genau wie heute im Sell-Flow).
„analog WBTC" = Frankencoin-Position (Richtung: Variante B)
Das Verlinken der Frankencoin-Repos weist die Richtung auf das Frankencoin-Collateral-Modell: REALU wird als Collateral in eine Frankencoin-Position eingebracht (analog wie WBTC), ZCHF wird geliehen und via bestehendem DFX-Off-Ramp aufs Bankkonto ausgezahlt. Weil REALU→ZCHF bereits Teil des Verkaufs-Flows ist, wird maximal Bestehendes wiederverwendet.
Die Alternative (A) rein zentral/DFX-vermittelt (DFX hält Collateral, eigenes Loan-Buch, kein Frankencoin) bleibt als Vergleichsmassstab/Fallback in der Analyse — sie ist aber nicht die signalisierte Richtung.
Beteiligte Repositories / Elemente
| Element |
Rolle |
Zugriff durch |
| RealUnitCH/app |
Wallet-App (Flutter). Kredit-Einstieg + Screen/Cubit/Service/DTO, Collateral-Signing (REALU-ERC-20), Loan-/Positions-Status, Rückzahlung. Kein Kredit-Code heute. |
spricht nur api.dfx.swiss |
| DFXswiss/api |
Integrations- & Autoritätsschicht (Kern dieser Phase). Neue Loan-/Position-Endpoints; kapselt Frankencoin-Reads + Tx-Bau; Off-Ramp-Auszahlung; Loan-Domänenlogik. Hat RealUnit-Subdomain + Payout-Infra, kein Loan-Modul. |
von der App |
| Frankencoin-ZCHF/Frankencoin |
Die Contracts (Write/On-Chain-Mechanik): MintingHub, Position, Frankencoin. |
nur via DFX-API (Tx-Bau/Broadcast) |
| Frankencoin-ZCHF/frankencoin-api |
NestJS-Read-API auf api.frankencoin.com; aufbereitete Positionen/Kurse/Stats (konsumiert den Indexer + CoinGecko). |
nur via DFX-API |
| Frankencoin-ZCHF/ponder |
Indexer (Ponder) auf ponder.frankencoin.com; indiziert Frankencoin-Events (multichain Mainnet/Polygon) → DB. Datenquelle für die frankencoin-api. |
nur via DFX-API |
Frankencoin-Mechanik (Voruntersuchung — das, was die DFX-API kapselt)
Aus den Contracts (Frankencoin-ZCHF/Frankencoin, Top-Level-Version gelesen — deployte V2-Adressen nicht einzeln verifiziert):
- Position eröffnen:
MintingHub.openPosition(...) (in contracts/minting/MintingHub.sol) erzeugt via PositionFactory einen Position-Contract, zieht OPENING_FEE = 1000 ZCHF, transferiert Collateral hinein. Mindestgrösse ~5000 ZCHF-Gegenwert. Frontend-Referenz: frankencoin-dapp pages/mint/create.tsx.
- Parameter setzt der Eröffner selbst:
liqPrice (Liquidationspreis), riskPremium (PPM Zins-Aufschlag; effektiver Zins = Leadrate + riskPremium, als upfront fee), reservePPM (Borrower-Reserve), minCollateral, mintingMaximum, initPeriod (≥3 Tage Veto, UI-Default 5), expiration, challengePeriod („länger für illiquides Collateral").
- Minten/Leihen:
Position.mint(); ausgezahlt = totalMint*(1e6 − reserve − fee)/1e6. LTV-Invariante collateral*price >= minted*1e18.
- Oracle-frei (bestätigt, README): Kein Price-Feed.
Position.price wird vom Owner gesetzt; Marktkorrektur nur über Challenges.
- Liquidation = Challenge/Dutch-Auction: beliebiger Challenger hinterlegt Collateral →
bid → entweder ChallengeAverted (Position gesund) oder Dutch-Auction → ChallengeSucceeded (Collateral verkauft, Schuld getilgt, CHALLENGER_REWARD = 2%). Fehlbetrag deckt zuerst Borrower-Reserve, dann die Pool-/FPS-Halter (coverLoss).
- Neues Collateral zulassen: Ist der
MintingHub aktiver Minter, kann jeder ohne Whitelist ein beliebiges ERC-20 via openPosition als Collateral nutzen (permissionless), mit positionsweisem Veto-Fenster (Position.deny). Ein eigenes Minter-Modul ginge über Frankencoin.suggestMinter(...) mit MIN_APPLICATION_PERIOD = 14 Tagen Veto durch qualifizierte FPS-Halter.
Kernrisiko für REALU: Das oracle-freie Challenge-Modell setzt einen liquiden Markt + Arbitrage-Anreiz voraus. REALU ist (vermutlich) illiquide und hat keinen öffentlichen Preis-Feed (Kurs nur via Brokerbot). Folge: unterbesicherte REALU-Positionen werden evtl. nie gechallenged → Bad Debt landet bei FPS-Haltern. Der Brokerbot-Kurs ist gerade nicht, was Frankencoin nutzt. Ob REALU im offiziellen Frankencoin-System überhaupt erwünscht/duldbar ist (soziale Governance der FPS-Halter), ist offen — eventuell braucht es einen eigenen, DFX-betriebenen Hub/Fork statt des Live-Mainnet-Systems.
Schritt 0 — Pflicht-Tiefenanalyse (vor jeder Implementierung)
A. RealUnitCH/app (Wallet, reiner Client)
- Sell-Flow als bestes Muster (
lib/packages/service/dfx/real_unit_sell_payment_info_service.dart, Kette PUT /v1/realunit/sell → …/unsigned-transactions → …/broadcast → …/confirm): er kombiniert on-chain Token-Bewegung und Bankauszahlung — exakt die zwei Loan-Bausteine. Was ist wiederverwendbar?
- Schichtung Screen→Cubit→Service→DTO (
lib/screens/buy/, lib/screens/sell/, …/cubits/buy_confirm/buy_confirm_cubit.dart).
- EVM-Signing: REALU-Approve/Transfer + ggf.
MintingHub-Calls als unsigned-tx empfangen, signieren, zurückgeben (lib/packages/wallet/eip712_signer.dart, eip7702_signer.dart, web3dart; BitBox-Pfad). Asset-Konstanten lib/packages/utils/default_assets.dart.
- Loan-/Positions-Statusanzeige + Rückzahlungs-Flow: rein über DFX-API-DTOs rendern.
- Navigation: Einstieg „Kredit" (
lib/setup/routing/routes/app_routes.dart + router_config.dart).
B. DFXswiss/api (Integrations- & Autoritätsschicht — Kern)
- RealUnit-Subdomain (
src/subdomains/supporting/realunit/) als Muster (sell/buy/registration); kein Loan-DTO/-Service vorhanden → neu.
- Frankencoin-Reads kapseln: welche Endpoints von
api.frankencoin.com (bzw. ponder.frankencoin.com) liefern Positions-/Kurs-/Status-Daten, die die DFX-API für die App aufbereitet? Genau auflisten.
- Frankencoin-Writes kapseln: unsigned-Tx-Bau für
openPosition/mint/repay/Rückzahlung mit REALU-Collateral; Broadcast wie im Sell-Flow.
- Off-Ramp: geliehenes ZCHF → Fiat aufs Bankkonto über bestehende Payout-Infra (
src/subdomains/supporting/payout/). Lücken?
- Loan-Domänenmodell: LTV/liqPrice, Zins, Laufzeit, Zustände, Margin-Call/Liquidationswarnung; was muss DFX-seitig persistiert/überwacht werden (Position-Monitoring)?
- Eigenes Backend vs. Frankencoin-Live-System: Entscheidung, ob DFX die öffentliche Frankencoin-Infra nutzt oder einen eigenen Hub/Fork betreibt (siehe Kernrisiko REALU).
C. Frankencoin-Elemente (extern — NUR über DFX-API anzusprechen)
Frankencoin Contracts: exakte openPosition/mint/repay/challenge-Signaturen + deployte V2-Adressen verifizieren.
frankencoin-api (api.frankencoin.com): verfügbare Read-Endpoints (Positionen, Preise, Stats) dokumentieren.
ponder (ponder.frankencoin.com): welche indizierten Daten, multichain-Setup (Mainnet relevant).
- Klären, ob REALU als Collateral im offiziellen System zulässig ist (permissionless
openPosition vs. suggestMinter 14-Tage-Veto vs. eigener DFX-Hub).
Bekannter Ist-Stand (Voruntersuchung — Startpunkt, nicht abschliessend)
| Baustein |
App |
DFXswiss/api |
Frankencoin |
| Kredit-Feature |
🔴 nicht vorhanden (Grep loan/credit/collateral leer) |
🔴 kein Loan-Modul |
🟢 Position/Mint-Mechanik produktiv |
| On-chain Token-Bewegung + Signing |
🟢 EIP-712/7702 + REALU-Transfer aus Sell-Flow |
🟢 unsigned-tx/broadcast-Pfad |
— |
| Off-Ramp ZCHF→Bankkonto |
🟢 Confirm-Muster |
🟡 Payout-Infra (auf Verkaufserlös ausgelegt) |
— |
| Collateral-Bewertung |
🟡 Brokerbot-Kurs (kein öffentl. Feed) |
🟡 Brokerbot/Pricing |
🔴 oracle-frei, Owner setzt Preis (REALU-Risiko) |
| Loan-Domänenlogik (LTV/Zins/Liquidation) |
🔴 fehlt |
🔴 fehlt |
🟢 im Contract (Challenge-Modell) |
Loan-Flow-Skizze (analog Sell-Flow, alles über api.dfx.swiss — Diskussionsgrundlage)
- Kredit anfragen → DFX-API liefert Konditionen (Max-Betrag aus Collateral/liqPrice, Zins, benötigtes REALU).
- Collateral-Lock + Mint → DFX-API baut unsigned-tx (
openPosition/mint mit REALU) → App signiert → DFX broadcastet.
- Auszahlung → DFX-API löst Off-Ramp ZCHF→Bankkonto aus.
- Rückzahlung → Fiat-Einzahlung → DFX-API tilgt Position (
repay) → Collateral-Freigabe.
- Risiko → DFX-API überwacht liqPrice/Challenge-Status, warnt den Nutzer.
Offene Fragen, die die Analyse beantworten muss (NICHT vorab entscheiden)
- Offizielles Frankencoin-System vs. eigener DFX-Hub/Fork — wegen REALU-Illiquidität + oracle-freiem Challenge-Risiko (Bad Debt zu FPS-Haltern).
- Custody: Non-Custodial-Anspruch — der Position-Owner (= User-Wallet) kontrolliert die Position on-chain; reicht das, oder braucht DFX Eingriffsrechte (Liquidationsschutz)?
- Preisbildung: REALU hat keinen öffentlichen Feed — wie wird
liqPrice konservativ gesetzt (Brokerbot als de-facto-Oracle widerspricht dem Design)?
- LTV/Zins/Laufzeit/Liquidationswarnung — wo berechnet/überwacht (DFX-API), wie wird der Nutzer gewarnt?
- Rückzahlung: Fiat-Einzahlung → Collateral-Freigabe; Teilrückzahlung; vorzeitige Tilgung.
- Regulatorik (potenziell grösster Blocker): Kreditvergabe gegen Token-Pfand lizenz-/auflagenpflichtig? Früh mit Legal & Compliance (David Lehner, RealUnit) klären — als Go/No-Go.
- KYC-Level für Kreditnehmer.
Deliverable von Schritt 0
Eine entschiedene Architektur (offizielles Frankencoin vs. eigener Hub; Custody-Modell) plus ein konkreter, abgestimmter Implementierungsplan (gerne Folge-Issues), inkl. klarer Aufteilung welche Änderung in welches Repo gehört — wobei alle App↔Backend-Kommunikation über api.dfx.swiss läuft — und ein dokumentiertes Go/No-Go aus Legal/Compliance. Erst dann Implementierung.
Workflow
- Analyse-Ergebnis + Architekturentscheid + Plan hier im Issue (oder verlinkten Sub-Issues) dokumentieren, bevor Code entsteht.
- Implementierung später:
RealUnitCH/app → Feature-Branch → PR gegen staging (Draft); DFXswiss/api → Feature-Branch → PR gegen develop (Draft). 3-Subagent-Review. DEV/PRD-Parität beachten.
Kontext & Ziel
Umsetzung von Phase 3 des RealUnit-App-Projekts: ein Kredit-/Lombard-Feature. Der Nutzer hinterlegt RealUnit-Token (REALU) als Sicherheit (Collateral) — vertraglich „analog WBTC" — und erhält im Gegenzug einen Stablecoin-Kredit, der via DFX-Off-Ramp aufs Bankkonto ausgezahlt wird. Die Rückzahlung gibt das Collateral wieder frei.
Der Auftraggeber hat Frankencoin (ZCHF) als Referenzmodell für „analog WBTC" benannt: ein oracle-freies, collateralized-minting-System, in dem ein beliebiges ERC-20 (heute u.a. WBTC) als Pfand dient, um ZCHF zu leihen.
🔒 Architektur-Leitplanke (HART, nicht verhandelbar)
Die RealUnit-App spricht ausschliesslich mit
api.dfx.swiss— niemals direkt mit Frankencoin, Ponder, einem Chain-RPC oder einem anderen Drittservice. Die DFX-API ist die alleinige Vermittlungs-/Autoritätsschicht. Sie nutzt ihrerseitsapi.frankencoin.com/ponder.frankencoin.comals Datenquelle oder betreibt ein eigenes passendes Backend. Die App bleibt reine Client-/Rendering-Schicht (vgl. API-as-Decision-Authority).Konsequenz: Jede Frankencoin-Interaktion (Reads wie Writes) wird hinter neuen
api.dfx.swiss-Endpoints gekapselt. Die App kennt Frankencoin nicht — sie kennt nur RealUnit-/DFX-Loan-Endpoints. Tx-Signing bleibt im Wallet (non-custodial), aber der unsigned-Tx-Bau + Broadcast laufen über die DFX-API (genau wie heute im Sell-Flow).„analog WBTC" = Frankencoin-Position (Richtung: Variante B)
Das Verlinken der Frankencoin-Repos weist die Richtung auf das Frankencoin-Collateral-Modell: REALU wird als Collateral in eine Frankencoin-Position eingebracht (analog wie WBTC), ZCHF wird geliehen und via bestehendem DFX-Off-Ramp aufs Bankkonto ausgezahlt. Weil REALU→ZCHF bereits Teil des Verkaufs-Flows ist, wird maximal Bestehendes wiederverwendet.
Die Alternative (A) rein zentral/DFX-vermittelt (DFX hält Collateral, eigenes Loan-Buch, kein Frankencoin) bleibt als Vergleichsmassstab/Fallback in der Analyse — sie ist aber nicht die signalisierte Richtung.
Beteiligte Repositories / Elemente
api.dfx.swissMintingHub,Position,Frankencoin.api.frankencoin.com; aufbereitete Positionen/Kurse/Stats (konsumiert den Indexer + CoinGecko).ponder.frankencoin.com; indiziert Frankencoin-Events (multichain Mainnet/Polygon) → DB. Datenquelle für die frankencoin-api.Frankencoin-Mechanik (Voruntersuchung — das, was die DFX-API kapselt)
Aus den Contracts (
Frankencoin-ZCHF/Frankencoin, Top-Level-Version gelesen — deployte V2-Adressen nicht einzeln verifiziert):MintingHub.openPosition(...)(incontracts/minting/MintingHub.sol) erzeugt viaPositionFactoryeinenPosition-Contract, ziehtOPENING_FEE = 1000 ZCHF, transferiert Collateral hinein. Mindestgrösse ~5000 ZCHF-Gegenwert. Frontend-Referenz:frankencoin-dapppages/mint/create.tsx.liqPrice(Liquidationspreis),riskPremium(PPM Zins-Aufschlag; effektiver Zins =Leadrate + riskPremium, als upfront fee),reservePPM(Borrower-Reserve),minCollateral,mintingMaximum,initPeriod(≥3 Tage Veto, UI-Default 5),expiration,challengePeriod(„länger für illiquides Collateral").Position.mint(); ausgezahlt =totalMint*(1e6 − reserve − fee)/1e6. LTV-Invariantecollateral*price >= minted*1e18.Position.pricewird vom Owner gesetzt; Marktkorrektur nur über Challenges.bid→ entweder ChallengeAverted (Position gesund) oder Dutch-Auction → ChallengeSucceeded (Collateral verkauft, Schuld getilgt,CHALLENGER_REWARD = 2%). Fehlbetrag deckt zuerst Borrower-Reserve, dann die Pool-/FPS-Halter (coverLoss).MintingHubaktiver Minter, kann jeder ohne Whitelist ein beliebiges ERC-20 viaopenPositionals Collateral nutzen (permissionless), mit positionsweisem Veto-Fenster (Position.deny). Ein eigenes Minter-Modul ginge überFrankencoin.suggestMinter(...)mitMIN_APPLICATION_PERIOD = 14 TagenVeto durch qualifizierte FPS-Halter.Kernrisiko für REALU: Das oracle-freie Challenge-Modell setzt einen liquiden Markt + Arbitrage-Anreiz voraus. REALU ist (vermutlich) illiquide und hat keinen öffentlichen Preis-Feed (Kurs nur via Brokerbot). Folge: unterbesicherte REALU-Positionen werden evtl. nie gechallenged → Bad Debt landet bei FPS-Haltern. Der Brokerbot-Kurs ist gerade nicht, was Frankencoin nutzt. Ob REALU im offiziellen Frankencoin-System überhaupt erwünscht/duldbar ist (soziale Governance der FPS-Halter), ist offen — eventuell braucht es einen eigenen, DFX-betriebenen Hub/Fork statt des Live-Mainnet-Systems.
Schritt 0 — Pflicht-Tiefenanalyse (vor jeder Implementierung)
A. RealUnitCH/app (Wallet, reiner Client)
lib/packages/service/dfx/real_unit_sell_payment_info_service.dart, KettePUT /v1/realunit/sell→…/unsigned-transactions→…/broadcast→…/confirm): er kombiniert on-chain Token-Bewegung und Bankauszahlung — exakt die zwei Loan-Bausteine. Was ist wiederverwendbar?lib/screens/buy/,lib/screens/sell/,…/cubits/buy_confirm/buy_confirm_cubit.dart).MintingHub-Calls als unsigned-tx empfangen, signieren, zurückgeben (lib/packages/wallet/eip712_signer.dart,eip7702_signer.dart,web3dart; BitBox-Pfad). Asset-Konstantenlib/packages/utils/default_assets.dart.lib/setup/routing/routes/app_routes.dart+router_config.dart).B. DFXswiss/api (Integrations- & Autoritätsschicht — Kern)
src/subdomains/supporting/realunit/) als Muster (sell/buy/registration); kein Loan-DTO/-Service vorhanden → neu.api.frankencoin.com(bzw.ponder.frankencoin.com) liefern Positions-/Kurs-/Status-Daten, die die DFX-API für die App aufbereitet? Genau auflisten.openPosition/mint/repay/Rückzahlung mit REALU-Collateral; Broadcast wie im Sell-Flow.src/subdomains/supporting/payout/). Lücken?C. Frankencoin-Elemente (extern — NUR über DFX-API anzusprechen)
FrankencoinContracts: exakteopenPosition/mint/repay/challenge-Signaturen + deployte V2-Adressen verifizieren.frankencoin-api(api.frankencoin.com): verfügbare Read-Endpoints (Positionen, Preise, Stats) dokumentieren.ponder(ponder.frankencoin.com): welche indizierten Daten, multichain-Setup (Mainnet relevant).openPositionvs.suggestMinter14-Tage-Veto vs. eigener DFX-Hub).Bekannter Ist-Stand (Voruntersuchung — Startpunkt, nicht abschliessend)
Loan-Flow-Skizze (analog Sell-Flow, alles über api.dfx.swiss — Diskussionsgrundlage)
openPosition/mintmit REALU) → App signiert → DFX broadcastet.repay) → Collateral-Freigabe.Offene Fragen, die die Analyse beantworten muss (NICHT vorab entscheiden)
liqPricekonservativ gesetzt (Brokerbot als de-facto-Oracle widerspricht dem Design)?Deliverable von Schritt 0
Eine entschiedene Architektur (offizielles Frankencoin vs. eigener Hub; Custody-Modell) plus ein konkreter, abgestimmter Implementierungsplan (gerne Folge-Issues), inkl. klarer Aufteilung welche Änderung in welches Repo gehört — wobei alle App↔Backend-Kommunikation über
api.dfx.swissläuft — und ein dokumentiertes Go/No-Go aus Legal/Compliance. Erst dann Implementierung.Workflow
RealUnitCH/app→ Feature-Branch → PR gegenstaging(Draft);DFXswiss/api→ Feature-Branch → PR gegendevelop(Draft). 3-Subagent-Review. DEV/PRD-Parität beachten.