Kurs budowy bota na Polymarket · Rozdział 9 z 32
Czytaj dane on-chain Polymarket bezpośrednio: salda USDC/pUSD, odczyty kontraktu CTF dla podaży wyników, zdarzenia UMA Optimistic Oracle proposed/disputed oraz logi transakcji Polygon - z kodem.
Co obejmuje ten rozdział
API Polymarket są wygodne, ale ostatecznie spójne. Łańcuch jest źródłem prawdy. Ten rozdział prowadzi przez odczyty on-chain, których bot produkcyjny używa do weryfikacji własnej księgowości: salda pUSD, zapas tokenów wyniku, zdarzenia sporu UMA oraz stan kontraktu CTF. Wzorzec, do którego dochodzi większość botów produkcyjnych, to najpierw API dla szybkości, a następnie okresowe uzgadnianie on-chain dla poprawności.
To jest rozdział 9 naszej 32-częściowej serii o budowie bota tradingowego na Polymarket. Omawiamy temat szczegółowo w sekcjach poniżej. Treść każdej sekcji jest pisana i publikowana rozdział po rozdziale; odpowiedzi FAQ i sekcje referencyjne są już kompletne i odzwierciedlają doświadczenie produkcyjne z prowadzenia naszego własnego bota.
- Co żyje on-chain (a co w CLOB)
- Adres kontraktu pUSD i ABI
- Conditional Tokens Framework (CTF)
- UMA Optimistic Oracle: zdarzenia proposed i disputed
- Odczyt logów zdarzeń Polygon (web3.py / ethers)
- Kiedy czytać on-chain, a kiedy ufać API
- Kod: wykrywanie sporu UMA przez subskrypcję zdarzeń
Co żyje on-chain (a co w CLOB)
Dwie maszyny stanów, dwie prawdy.
On-chain (Polygon): salda pUSD, zapas tokenów wyniku (podaż ERC-1155 na token), zatwierdzenia (allowance), propozycje i spory w UMA Optimistic Oracle, zdarzenia wpłat i wypłat. Ostatecznie poprawne; opóźnienie to jeden blok Polygon (~2 sekundy).
CLOB (API Polymarket): arkusz zleceń, ostatnie transakcje, oczekujące zlecenia z limitem, potwierdzenia dopasowania. W czasie rzeczywistym, ale ostatecznie spójne - dopasowanie jest potwierdzane, zanim ERC-1155 zostanie rozliczony, co powoduje problem fantomowej realizacji omawiany w rozdziale 12.
Oba powinny zawsze się zbiegać. Gdy się rozchodzą, łańcuch jest źródłem prawdy. Bot, który ufa wyłącznie CLOB, będzie dryfował; bot, który ufa wyłącznie łańcuchowi, będzie handlował wolno. Kod produkcyjny używa obu: CLOB do decyzji krytycznych dla szybkości, łańcucha do okresowego uzgadniania.
Adres kontraktu pUSD i ABI
pUSD to opakowany stablecoin Polymarket używany od migracji do V2 w 2026 roku. Kontrakt pod adresem 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB na Polygon mainnet jest standardowym ERC-20.
Trzy odczyty, które mają znaczenie dla bota:
balanceOf(proxy)- twoje pUSD, które możesz wydać. Porównuj z widokiem salda w CLOB przy każdym restarcie.allowance(proxy, exchange_contract)- czy kontrakty giełdy CTF/NegRisk mogą wydawać twoje pUSD. Wymagane do dopasowania zlecenia.- Zdarzenie
Transfer- wykrywa wpłaty i wypłaty bez odpytywania.
USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) pozostaje parą do wyjścia (off-ramp). Większość botów potrzebuje tylko odczytów pUSD; USDC ma znaczenie wyłącznie podczas cykli wpłat/wypłat.
Conditional Tokens Framework (CTF)
Udziały wyniku to tokeny ERC-1155 wybijane przez Conditional Tokens Framework (CTF) Gnosis. Kontrakt CTF na Polygon pod adresem 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 śledzi podaż dla każdego position-id.
Trzy odczyty:
balanceOf(proxy, position_id)- ile tokenów wyniku faktycznie posiadasz dla danego rynku i wyniku.getOutcomeSlotCount(condition_id)- liczba wyników (2 dla binarnego, N dla wielowynikowego).payoutNumerators,payoutDenominator- ustawiane, gdy UMA rozstrzyga rynek. Odczyt tych wartości mówi ci, która strona wygrała, zanim interfejs CLOB się zaktualizuje.
position_id to hash z (condition_id, outcome_index). Oblicz go po stronie klienta przez pomocnika getPositionId w CTF albo odtwórz matematykę keccak w swoim stosie.
UMA Optimistic Oracle: zdarzenia proposed i disputed
UMA Optimistic Oracle (OO) obsługuje całe rozstrzyganie sporów w Polymarket. Dwa zdarzenia, na które bot może chcieć się zasubskrybować.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)- wywoływane, gdy bot Polymarket proponuje wynik.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)- wywoływane, gdy ktoś kwestionuje zaproponowany wynik.
Adres kontraktu OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filtruj po adresie requester Polymarket.
Dlaczego warto się zasubskrybować: spór oznacza, że rozstrzygnięcie jest teraz kwestionowane i będzie wymagało głosowania UMA przez 24-72 h. W tym oknie rynek może zostać wstrzymany do handlu. Bot trzymający pozycje na rynku ze sporem powinien wiedzieć o tym natychmiast.
Odczyt logów zdarzeń Polygon (web3.py / ethers)
Dwie referencyjne implementacje.
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 });
});
Do ciągłego monitorowania używaj transportu WebSocket (wss://...) zamiast odpytywania HTTP - mniej żądań i szybsze dostarczanie. Większość płatnych dostawców RPC ma WebSocket już w planach startowych.
Kiedy czytać on-chain, a kiedy ufać API
Praktyczne zasady z produkcji.
- Ufaj API dla: arkusza zleceń w czasie rzeczywistym, ostatnich transakcji, twoich własnych oczekujących zleceń, metadanych rynku (slug, pytanie, data zakończenia), odkrywania wydarzeń/rynków.
- Ufaj łańcuchowi dla: twojego własnego salda pUSD, twojego zapasu tokenów wyniku, weryfikacji wpłat i wypłat, wyników rozstrzygnięcia, stanu sporu.
- Sprawdzaj oba dla: wszystkiego finansowego, co bot zapisał jako realizację - dopasuj zdarzenie „matched" z API do transferu CTF w łańcuchu, aby potwierdzić rozliczenie.
Zasada 5-sekundowego oczekiwania po kupnie (rozdział 12) to rzeczywistość on-chain wkraczająca do czasu API. Sprzedaż GTC wysłana natychmiast po kupnie rynkowym zobaczy z odczytu łańcucha balance: 0, mimo że CLOB potraktował dopasowanie chwilę wcześniej.
Kod: wykrywanie sporu UMA przez subskrypcję zdarzeń
Referencja: śledź spory UMA związane z Polymarket w czasie rzeczywistym.
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)
Pole ancillaryData to zakodowany szesnastkowo tekst w stylu JSON zawierający pytanie rynku. Jego dekodowanie daje identyfikator równoważny slugowi, który można skorelować z otwartymi pozycjami.


