Polymarket Bot Tutorial · Chương 9 trong 32

Đọc dữ liệu on-chain của Polymarket trực tiếp: số dư USDC/pUSD, CTF contract đọc supply của outcome, các event proposed/disputed của UMA Optimistic Oracle, và Polygon transaction logs - kèm code.

Chương này bao gồm gì

API của Polymarket rất tiện, nhưng về cuối cùng thì vẫn chỉ là eventually consistent. Chain mới là nguồn thẩm quyền. Chương này sẽ đi qua các on-chain reads mà một production bot dùng để tự kiểm tra bookkeeping của nó: số dư pUSD, inventory của outcome-token, các UMA dispute event, và state của CTF contract. Mẫu mà hầu hết production bot hội tụ về là API-first để nhanh, cộng với đối soát on-chain định kỳ để đảm bảo chính xác.

  • Cái gì nằm on-chain (vs trong CLOB)
  • Địa chỉ contract và ABI của pUSD
  • Conditional Tokens Framework (CTF)
  • UMA Optimistic Oracle: các event proposed và disputed
  • Đọc Polygon event logs (web3.py / ethers)
  • Khi nào đọc on-chain vs tin vào API
  • Code: phát hiện UMA dispute qua event subscription

Cái gì nằm on-chain (vs trong CLOB)

Hai state machine, hai sự thật.

On-chain (Polygon): số dư pUSD, inventory của outcome-token (supply ERC-1155 theo từng token), approval allowance, các proposal và dispute của UMA Optimistic Oracle, các event nạp và rút. Cuối cùng sẽ đúng; độ trễ là một Polygon block (~2 giây).

CLOB (Polymarket API): order book, giao dịch gần đây, pending limit order, xác nhận khớp lệnh. Realtime nhưng eventually consistent - một match được xác nhận trước khi ERC-1155 settle, tạo ra vấn đề phantom-fill sẽ được đề cập ở chương 12.

Hai bên luôn phải hội tụ về cùng một trạng thái. Khi chúng lệch nhau, chain là nguồn thẩm quyền. Một bot chỉ tin CLOB sẽ bị lệch dần; một bot chỉ tin chain sẽ trade chậm. Code production dùng cả hai: CLOB cho quyết định cần tốc độ, chain cho đối soát định kỳ.

Địa chỉ contract và ABI của pUSD

pUSD là stablecoin wrapper của Polymarket được dùng từ sau migration V2 năm 2025. Contract tại 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB trên Polygon mainnet là một ERC-20 tiêu chuẩn.

Ba thao tác đọc quan trọng với một bot:

  • balanceOf(proxy) - pUSD có thể chi tiêu của bạn. So sánh với góc nhìn của CLOB về số dư của bạn sau mỗi lần khởi động lại.
  • allowance(proxy, exchange_contract) - các contract exchange của CTF/NegRisk có thể dùng pUSD của bạn hay không. Cần cho việc match order.
  • Subscription event Transfer - phát hiện nạp và rút mà không cần polling.

USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) vẫn là cặp off-ramp. Phần lớn bot chỉ cần đọc pUSD; USDC chỉ quan trọng trong các chu kỳ nạp/rút.

Conditional Tokens Framework (CTF)

Outcome shares là token ERC-1155 được mint bởi Conditional Tokens Framework (CTF) của Gnosis. Contract CTF trên Polygon tại 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 theo dõi supply theo từng position-id.

Ba thao tác đọc:

  • balanceOf(proxy, position_id) - bạn thực sự đang giữ bao nhiêu outcome token cho market+outcome đó.
  • getOutcomeSlotCount(condition_id) - số lượng outcomes (2 cho binary, N cho multi-outcome).
  • payoutNumerators, payoutDenominator - được set khi UMA resolve market. Đọc chúng sẽ cho bạn biết bên nào thắng trước khi UI của CLOB cập nhật.

position_id là một hash của (condition_id, outcome_index). Tính nó phía client qua helper getPositionId của CTF hoặc tái tạo phép tính keccak trong stack của bạn.

UMA Optimistic Oracle: các event proposed và disputed

UMA Optimistic Oracle (OO) xử lý toàn bộ việc dispute resolution của Polymarket. Có hai event mà bot của bạn có thể muốn subscribe.

  • ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice) - được bắn khi một Polymarket bot đề xuất một outcome.
  • DisputePrice(requester, identifier, timestamp, ancillaryData, disputer) - được bắn khi ai đó thách thức outcome đã đề xuất.

Địa chỉ contract OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Lọc theo địa chỉ requester của Polymarket.

Tại sao nên subscribe: một dispute có nghĩa là resolution giờ đã bị tranh chấp và sẽ cần một vote UMA trong 24-72 giờ. Trong khoảng thời gian đó, market có thể bị tạm dừng giao dịch. Một bot đang giữ vị thế ở market bị dispute nên biết ngay lập tức.

Đọc Polygon event logs (web3.py / ethers)

Hai implementation tham chiếu.

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 });
});

Để giám sát liên tục, hãy dùng WebSocket transport (wss://...) thay vì HTTP polling - ít request hơn và nhận dữ liệu nhanh hơn. Hầu hết các RPC provider trả phí đều có WebSocket ở các gói entry tier.

Khi nào đọc on-chain vs tin vào API

Các quy tắc thực tế từ production.

  • Tin vào API cho: order book realtime, giao dịch gần đây, pending order của chính bạn, metadata của market (slug, question, end date), khám phá event/market.
  • Tin vào chain cho: số dư pUSD của chính bạn, inventory outcome-token của chính bạn, xác minh nạp và rút, kết quả resolution, trạng thái dispute.
  • Đối chiếu cả hai cho: bất kỳ thứ gì liên quan đến tài chính mà bot ghi nhận là một fill - hãy khớp event "matched" của API với transfer CTF trên chain để xác nhận settlement.

Quy tắc chờ 5 giây sau khi mua (chương 12) là thực tế on-chain xen vào thời gian của API. Một lệnh sell GTC được gửi ngay sau khi mua market sẽ thấy balance: 0 từ kiểm tra chain, dù CLOB vừa match vài khoảnh khắc trước đó.

Code: phát hiện UMA dispute qua event subscription

Tham khảo: theo dõi các dispute liên quan đến Polymarket của UMA theo thời gian thực.

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)

Trường ancillaryData là text dạng JSON-ish được mã hóa hex, chứa market question. Giải mã nó sẽ cho bạn identifier tương đương slug để đối chiếu với các vị thế đang mở của bạn.

Các câu hỏi thường gặp

Tôi có thể tìm contract pUSD của Polymarket ở đâu?
pUSD nằm tại 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB trên Polygon. Nó đã thay thế USDC.e làm collateral mặc định của Polymarket vào ngày 28 tháng 4 năm 2026. Link Polygonscan: https://polygonscan.com/token/0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB.
CTF contract trên Polymarket là gì?
CTF là viết tắt của Conditional Tokens Framework - contract ERC-1155 có nguồn gốc từ Gnosis, phát hành outcome token (các share YES và NO mà bạn trade). Mỗi market của Polymarket là một CTF position với logic redemption gắn với resolution của UMA. Bot hiếm khi cần tương tác trực tiếp với CTF; SDK sẽ xử lý việc đó.
Tôi subscribe UMA dispute events trên Polygon như thế nào?
Subscribe vào các event "ProposePrice" và "DisputePrice" của UMA Optimistic Oracle V2 contract thông qua WebSocket của nhà cung cấp Polygon RPC của bạn. Lọc theo trường requesterAddress (Polymarket oracle adapter) để chỉ lấy các dispute liên quan đến Polymarket. Xem code mẫu trong chương này.
Tôi có cần đọc dữ liệu on-chain nếu đã tin Polymarket API không?
Với hầu hết chiến lược, là không. CLOB API là nguồn thẩm quyền cho dữ liệu order book và trade, còn gamma API là nguồn thẩm quyền cho metadata. Bạn chỉ đọc on-chain khi cần (a) cảnh báo UMA dispute sớm hơn API hiển thị, (b) xác minh rằng một khoản nạp thực sự đã tới, hoặc (c) analytics tùy chỉnh trên outcomes/positions.
Độ trễ của dữ liệu on-chain Polygon so với Polymarket API là bao nhiêu?
Block time của Polygon là khoảng 2 giây. Polymarket API thường hiển thị thay đổi order book trong vòng vài trăm mili giây sau match on-chain. Với hầu hết signals, API nhanh hơn việc tự đọc on-chain của bạn. UMA disputes là ngoại lệ - event on-chain bắn ra trước khi UI phản ánh dispute.
Tôi có thể đọc vị thế Polymarket mà không cần CLOB API không?
Về mặt kỹ thuật là có - hãy đọc CTF balanceOf(walletAddress, positionId) cho từng position. Trên thực tế, endpoint CLOB API /trade/positions nhanh hơn, có bao gồm pricing, và tổng hợp tất cả vị thế của bạn. Chỉ nên quay lại đọc on-chain nếu bạn cần xác minh hoặc API đang gặp sự cố.