Poradnik bota Polymarket · 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 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 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.


