Polymarket Bot Tutorial · Rozdział 9 z 32
Czytaj dane on-chain Polymarket bezpośrednio: salda USDC/pUSD, odczyty kontraktu CTF dla podaży outcome, 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, inventory tokenów outcome, 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 okresowa reconciliacja on-chain dla poprawności.
To jest rozdział 9 z naszej 32-częściowej serii o budowie Polymarket trading bota. Omawiamy temat szczegółowo w sekcjach poniżej. Treść główna dla każdej sekcji jest pisana i publikowana rozdział po rozdziale; odpowiedzi FAQ i referencje są już kompletne i odzwierciedlają doświadczenie produkcyjne z uruchamiania naszego własnego tradera.
- Co żyje on-chain (vs 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 event subscription
Co żyje on-chain (vs w CLOB]
Dwa state machines, dwie prawdy.
On-chain (Polygon): salda pUSD, inventory tokenów outcome (supply ERC-1155 na token), approvals 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 (Polymarket API): order book, ostatnie transakcje, oczekujące limit orders, potwierdzenia match. Real-time, ale ostatecznie spójne — match jest potwierdzany, zanim ERC-1155 zostanie rozliczony, co powoduje problem phantom-fill 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 okresowej reconciliacji.
Adres kontraktu pUSD i ABI
pUSD to wrapper stablecoina Polymarket używany od migracji do V2 w 2025 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 exchange CTF/NegRisk mogą wydawać twoje pUSD. Wymagane do matching order.- Zdarzenie
Transfer— wykrywa wpłaty i wypłaty bez polling.
USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) pozostaje parą 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 outcome to tokeny ERC-1155 mintowane przez Conditional Tokens Framework (CTF) Gnosis. Kontrakt CTF na Polygon pod adresem 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 śledzi supply dla każdego position-id.
Trzy odczyty:
balanceOf(proxy, position_id)— ile tokenów outcome faktycznie posiadasz dla danego market+outcome.getOutcomeSlotCount(condition_id)— liczba outcome (2 dla binary, N dla multi-outcome).payoutNumerators,payoutDenominator— ustawiane, gdy UMA rozstrzyga market. Odczyt tych wartości mówi ci, która strona wygrała, zanim UI CLOB się zaktualizuje.
position_id to hash z (condition_id, outcome_index). Oblicz go po stronie klienta przez helper getPositionId w CTF albo odtwórz matematykę keccak w swoim stacku.
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ę subskrybować.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)— wywoływane, gdy bot Polymarket proponuje outcome.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)— wywoływane, gdy ktoś kwestionuje zaproponowany outcome.
Adres kontraktu OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filtruj po adresie requester Polymarket.
Dlaczego warto się subskrybować: spór oznacza, że rozstrzygnięcie jest teraz kwestionowane i będzie wymagało głosowania UMA przez 24-72h. W tym oknie market może zostać wstrzymany do handlu. Bot trzymający pozycje na marketcie 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 HTTP polling — mniej requestów i szybsze dostarczanie. Większość płatnych providerów RPC ma WebSocket już w planach startowych.
Kiedy czytać on-chain, a kiedy ufać API
Praktyczne zasady z produkcji.
- Ufaj API dla: order book w czasie rzeczywistym, ostatnie transakcje, twoje własne oczekujące orders, market metadata (slug, question, end date), discovery event/market.
- Ufaj łańcuchowi dla: twojego własnego salda pUSD, twojego inventory tokenów outcome, weryfikacji wpłat i wypłat, outcomes rozstrzygnięcia, stanu sporu.
- Sprawdzaj oba dla: wszystkiego finansowego, które bot zapisał jako fill — dopasuj event "matched" z API do transferu CTF w łańcuchu, aby potwierdzić settlement.
Zasada 5-sekundowego oczekiwania po buy (rozdział 12) to rzeczywistość on-chain wkraczająca do czasu API. GTC sell wysłany natychmiast po market buy zobaczy z checka łańcucha balance: 0, mimo że CLOB potraktował match chwilę wcześniej.
Kod: wykrywanie sporu UMA przez event subscription
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 hex-enkodowany tekst w stylu JSON zawierający pytanie marketu. Jego dekodowanie daje identyfikator równoważny slugowi, który można skorelować z otwartymi pozycjami.











