Polymarket Bot Tutorial · Kapitel 9 von 32
Lies Polymarket On-Chain-Daten direkt: USDC/pUSD-Balances, CTF-Contract-Reads für Outcome-Supply, UMA Optimistic Oracle Proposed/Disputed Events und Polygon Transaction Logs - mit Code.
Was dieses Kapitel abdeckt
Die APIs von Polymarket sind bequem, aber letztlich eventual consistent. Die Chain ist die autoritative Quelle. Dieses Kapitel führt durch die On-Chain-Reads, die ein Production Bot nutzt, um die eigene Buchführung zu verifizieren: pUSD-Balances, Outcome-Token-Inventar, UMA-Dispute-Events und CTF-Contract-State. Das Muster, auf das sich die meisten Production Bots einigen, ist API-first für Geschwindigkeit plus periodische On-Chain-Reconciliation für Korrektheit.
- Was on-chain lebt (vs in CLOB)
- pUSD Contract Address und ABI
- Conditional Tokens Framework (CTF)
- UMA Optimistic Oracle: proposed and disputed events
- Polygon Event Logs lesen (web3.py / ethers)
- Wann on-chain lesen vs der API vertrauen
- Code: einen UMA-Dispute via Event-Subscription erkennen
Was on-chain lebt (vs in CLOB)
Zwei State Machines, zwei Wahrheiten.
On-chain (Polygon): pUSD-Balances, Outcome-Token-Inventar (ERC-1155 Supply pro Token), Allowance Approvals, UMA Optimistic Oracle Proposals und Disputes, Deposit- und Withdrawal-Events. Letztlich korrekt; die Latenz beträgt einen Polygon-Block (~2 Sekunden).
CLOB (Polymarket API): Order Book, recente Trades, Pending Limit Orders, Match-Acknowledgements. Echtzeitnah, aber eventual consistent - ein Match wird bestätigt, bevor sich das ERC-1155 Settlement abschließt, was das Phantom-Fill-Problem erzeugt, das in Kapitel 12 behandelt wird.
Beide sollten immer konvergieren. Wenn sie auseinanderlaufen, ist die Chain autoritativ. Ein Bot, der nur dem CLOB vertraut, driftet; ein Bot, der nur der Chain vertraut, handelt langsam. Production Code nutzt beides: CLOB für zeitkritische Entscheidungen, Chain für periodische Reconciliation.
pUSD Contract Address und ABI
pUSD ist Polymarkets Stablecoin-Wrapping, das seit der V2-Migration 2025 verwendet wird. Der Contract unter 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB auf Polygon Mainnet ist ein standardmäßiger ERC-20.
Drei Reads, die für einen Bot wichtig sind:
balanceOf(proxy)- dein ausgebbares pUSD. Vergleiche es bei jedem Neustart mit der Ansicht des CLOB über dein Guthaben.allowance(proxy, exchange_contract)- ob die CTF-/NegRisk-Exchange-Contracts dein pUSD ausgeben dürfen. Erforderlich für Order Matching.Transfer-Event-Subscription - erkennt Deposits und Withdrawals ohne Polling.
USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) bleibt das Off-Ramp-Paar. Die meisten Bots benötigen nur pUSD-Reads; USDC ist nur während Deposit-/Withdrawal-Zyklen relevant.
Conditional Tokens Framework (CTF)
Outcome Shares sind ERC-1155-Token, die vom Conditional Tokens Framework (CTF) von Gnosis gemintet werden. Der CTF-Contract auf Polygon unter 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 verfolgt den Supply pro Position-ID.
Drei Reads:
balanceOf(proxy, position_id)- wie viele Outcome-Token du für diesen Market+Outcome tatsächlich hältst.getOutcomeSlotCount(condition_id)- Anzahl der Outcomes (2 für Binary, N für Multi-Outcome).payoutNumerators,payoutDenominator- werden gesetzt, wenn UMA den Market resolved. Wenn du diese liest, weißt du, welche Seite gewonnen hat, bevor die CLOB-UI aktualisiert wird.
Die position_id ist ein Hash aus (condition_id, outcome_index). Berechne sie clientseitig über den getPositionId-Helper des CTF oder repliziere die keccak-Mathematik in deinem Stack.
UMA Optimistic Oracle: proposed and disputed events
UMAs Optimistic Oracle (OO) behandelt die gesamte Polymarket-Dispute-Resolution. Zwei Events, auf die dein Bot eventuell subscriben sollte.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)- wird ausgelöst, wenn ein Polymarket-Bot ein Outcome vorschlägt.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)- wird ausgelöst, wenn jemand das vorgeschlagene Outcome anfechtet.
OO Contract Address: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filtere nach der Requester-Address von Polymarket.
Warum subscriben: Ein Dispute bedeutet, dass die Resolution nun angefochten ist und eine 24-72h UMA-Abstimmung erfordert. In diesem Zeitfenster kann der Market für den Handel pausiert werden. Ein Bot, der Positionen in einem disputed Market hält, sollte das sofort wissen.
Polygon Event Logs lesen (web3.py / ethers)
Zwei Referenzimplementierungen.
Python (web3.py):
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(POLYGON_RPC))
ctf = w3.eth.contract(address=CTF_ADDR, abi=CTF_ABI)
filter = ctf.events.PayoutRedemption.create_filter(fromBlock="latest")
for event in filter.get_new_entries():
print(event["args"])
Node (ethers v6):
import { ethers } from "ethers";
const p = new ethers.JsonRpcProvider(POLYGON_RPC);
const ctf = new ethers.Contract(CTF_ADDR, CTF_ABI, p);
ctf.on("PayoutRedemption", (redeemer, collateral, parentId, conditionId) => {
console.log("redemption", { redeemer, conditionId });
});
Für kontinuierliches Monitoring verwende WebSocket-Transport (wss://...) statt HTTP-Polling - weniger Requests und schnellere Zustellung. Die meisten bezahlten RPC-Provider enthalten WebSocket bereits in den Einstiegstarifen.
Wann on-chain lesen vs der API vertrauen
Praktische Regeln aus der Produktion.
- Der API vertrauen für: Echtzeit-Order-Book, recente Trades, deine eigenen Pending Orders, Market-Metadaten (Slug, Question, Enddatum), Event-/Market-Discovery.
- Der Chain vertrauen für: dein eigenes pUSD-Guthaben, dein eigenes Outcome-Token-Inventar, Verifikation von Deposits und Withdrawals, Resolution-Outcomes, Dispute-Status.
- Beides abgleichen für: alles Finanzielle, das der Bot als Fill protokolliert hat - gleiche das API-„matched“-Event mit dem CTF-Transfer der Chain ab, um das Settlement zu bestätigen.
Die 5-Sekunden-Warte-Regel nach einem Buy (Kapitel 12) ist die On-Chain-Realität, die in die API-Zeit hineinragt. Ein GTC-Sell, der unmittelbar nach einem Market-Buy gesendet wird, sieht beim Chain-Check balance: 0, obwohl der CLOB das Match schon Sekunden zuvor bestätigt hat.
Code: einen UMA-Dispute via Event-Subscription erkennen
Referenz: Polymarket-bezogene UMA-Disputes in Echtzeit beobachten.
from web3 import Web3
w3 = Web3(Web3.WebsocketProvider(POLYGON_WSS))
oo = w3.eth.contract(address=UMA_OO_ADDR, abi=UMA_ABI)
POLY_REQUESTER = "0x..." # Polymarket's UMA requester address
def on_dispute(event):
args = event["args"]
if args["requester"].lower() != POLY_REQUESTER.lower(): return
print(f"DISPUTE on Polymarket market: ancillary={args['ancillaryData'][:40]}...")
# decode ancillaryData to recover the market question
event_filter = oo.events.DisputePrice.create_filter(fromBlock="latest")
while True:
for ev in event_filter.get_new_entries():
on_dispute(ev)
time.sleep(2)
Das Feld ancillaryData ist hex-kodierter JSON-ähnlicher Text, der die Market Question enthält. Wenn du ihn dekodierst, erhältst du den slug-ähnlichen Identifier, um ihn mit deinen offenen Positionen abzugleichen.










