Polymarket Bot Tutorial · Chapter 9 of 32
Baca data on-chain Polymarket secara langsung: saldo USDC/pUSD, pembacaan kontrak CTF untuk supply outcome, event UMA Optimistic Oracle yang proposed/disputed, dan Polygon transaction logs - lengkap dengan code.
Apa yang dibahas dalam chapter ini
API Polymarket itu praktis, tetapi pada akhirnya bersifat eventually consistent. Chain adalah sumber otoritatif. Chapter ini membahas pembacaan on-chain yang digunakan bot production untuk memverifikasi bookkeeping-nya sendiri: saldo pUSD, inventory outcome-token, event dispute UMA, dan state kontrak CTF. Pola yang paling umum dipakai bot production adalah API-first untuk kecepatan ditambah rekonsiliasi on-chain berkala untuk akurasi.
- Apa yang ada di on-chain (vs di CLOB)
- Alamat contract pUSD dan ABI
- Conditional Tokens Framework (CTF)
- UMA Optimistic Oracle: event proposed dan disputed
- Membaca Polygon event logs (web3.py / ethers)
- Kapan membaca on-chain vs mempercayai API
- Code: mendeteksi dispute UMA via event subscription
Apa yang ada di on-chain (vs di CLOB)
Dua state machine, dua kebenaran.
On-chain (Polygon): saldo pUSD, inventory outcome-token (supply ERC-1155 per token), approval allowance, proposal dan dispute UMA Optimistic Oracle, event deposit dan withdrawal. Pada akhirnya benar; latensinya satu Polygon block (~2 detik).
CLOB (Polymarket API): order book, trade terbaru, pending limit orders, acknowledgement match. Real-time tetapi eventually consistent - sebuah match diakui sebelum ERC-1155 settle, yang menimbulkan masalah phantom-fill yang dibahas di chapter 12.
Keduanya seharusnya selalu konvergen. Saat keduanya berbeda, chain adalah otoritatif. Bot yang hanya mempercayai CLOB akan melenceng; bot yang hanya mempercayai chain akan trading lambat. Code production menggunakan keduanya: CLOB untuk keputusan yang kritis terhadap kecepatan, chain untuk rekonsiliasi berkala.
Alamat contract pUSD dan ABI
pUSD adalah stablecoin wrapper Polymarket yang digunakan sejak migrasi V2 pada 2025. Contract di 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB pada Polygon mainnet adalah ERC-20 standar.
Tiga pembacaan yang penting untuk bot:
balanceOf(proxy)- pUSD yang bisa Anda belanjakan. Bandingkan dengan tampilan saldo CLOB pada setiap restart.allowance(proxy, exchange_contract)- apakah contract exchange CTF/NegRisk dapat membelanjakan pUSD Anda. Diperlukan untuk order matching.- Subscription event
Transfer- mendeteksi deposit dan withdrawal tanpa polling.
USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) tetap menjadi pair off-ramp. Kebanyakan bot hanya perlu pembacaan pUSD; USDC hanya penting saat siklus deposit/withdrawal.
Conditional Tokens Framework (CTF)
Outcome shares adalah token ERC-1155 yang dicetak oleh Conditional Tokens Framework (CTF) milik Gnosis. Contract CTF di Polygon pada 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 melacak supply per position-id.
Tiga pembacaan:
balanceOf(proxy, position_id)- berapa banyak outcome token yang benar-benar Anda pegang untuk market+outcome tersebut.getOutcomeSlotCount(condition_id)- jumlah outcome (2 untuk binary, N untuk multi-outcome).payoutNumerators,payoutDenominator- di-set ketika UMA menyelesaikan market. Membacanya memberi tahu Anda sisi mana yang menang sebelum UI CLOB diperbarui.
position_id adalah hash dari (condition_id, outcome_index). Hitung di sisi client melalui helper getPositionId dari CTF atau tirukan math keccak di stack Anda.
UMA Optimistic Oracle: event proposed dan disputed
Optimistic Oracle (OO) milik UMA menangani semua penyelesaian sengketa Polymarket. Ada dua event yang mungkin ingin Anda subscribe.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)- dipicu saat bot Polymarket mengusulkan outcome.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)- dipicu saat seseorang menantang outcome yang diusulkan.
Alamat contract OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filter berdasarkan alamat requester milik Polymarket.
Mengapa subscribe: dispute berarti resolusi sekarang diperdebatkan dan akan memerlukan voting UMA selama 24-72 jam. Selama jendela itu, market mungkin dipause untuk trading. Bot yang memegang posisi di market yang disengketakan harus mengetahuinya segera.
Membaca Polygon event logs (web3.py / ethers)
Dua implementasi referensi.
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 });
});
Untuk monitoring berkelanjutan gunakan transport WebSocket (wss://...) вместо HTTP polling - request lebih sedikit dan pengiriman lebih cepat. Sebagian besar paid RPC provider menyertakan WebSocket pada tier entry.
Kapan membaca on-chain vs mempercayai API
Aturan praktis dari production.
- Percayai API untuk: real-time order book, recent trades, pending orders Anda sendiri, metadata market (slug, question, end date), discovery event/market.
- Percayai chain untuk: saldo pUSD Anda sendiri, inventory outcome-token Anda sendiri, verifikasi deposit dan withdrawal, outcome resolusi, state dispute.
- Cross-check keduanya untuk: apa pun yang bersifat finansial dan dicatat bot sebagai fill - cocokkan event "matched" dari API dengan transfer CTF di chain untuk mengonfirmasi settlement.
Aturan tunggu 5 detik setelah buy (chapter 12) adalah kenyataan on-chain yang masuk ke waktu API. GTC sell yang dikirim segera setelah market buy akan melihat balance: 0 dari pengecekan chain meskipun CLOB sudah match beberapa saat sebelumnya.
Code: mendeteksi dispute UMA via event subscription
Referensi: memantau dispute UMA terkait Polymarket secara real time.
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)
Field ancillaryData adalah teks mirip JSON yang di-hex-encode dan berisi market question. Mendekodenya memberi Anda identifier setara slug untuk dicocokkan dengan open positions Anda.












