Polymarket Bot Tutorial · Chapter 9 of 32

داده‌های on-chain Polymarket را مستقیماً بخوانید: موجودی‌های USDC/pUSD، خواندن‌های CTF contract برای عرضه outcome، رویدادهای proposed/disputed در UMA Optimistic Oracle، و Polygon transaction logs - همراه با code.

این chapter چه چیزهایی را پوشش می‌دهد

APIهای Polymarket راحت هستند، اما در نهایت eventually consistent می‌شوند. chain مرجع نهایی است. این chapter خواندن‌های on-chain را مرور می‌کند که یک bot production برای verify کردن bookkeeping خودش از آن‌ها استفاده می‌کند: pUSD balances، موجودی outcome-token، رویدادهای UMA dispute، و وضعیت CTF contract. الگویی که بیشتر botهای production به آن می‌رسند این است: API-first برای سرعت، به‌همراه on-chain reconciliation دوره‌ای برای correctness.

  • چه چیزهایی on-chain هستند (در برابر CLOB)
  • آدرس contract و ABI برای pUSD
  • Conditional Tokens Framework (CTF)
  • UMA Optimistic Oracle: proposed و disputed events
  • خواندن Polygon event logs (web3.py / ethers)
  • چه زمانی on-chain بخوانیم و چه زمانی به API اعتماد کنیم
  • code: تشخیص UMA dispute با استفاده از event subscription

چه چیزهایی on-chain هستند (در برابر CLOB)

دو state machine، دو truth.

On-chain (Polygon): pUSD balances، موجودی outcome-token (عرضه ERC-1155 برای هر token)، allowance approvals، proposalها و disputeهای UMA Optimistic Oracle، و رویدادهای deposit و withdrawal. در نهایت correct می‌شود؛ latency آن برابر با یک Polygon block است (حدود 2 ثانیه).

CLOB (Polymarket API): order book، recent trades، pending limit orders، match acknowledgments. Real-time اما eventually consistent - یک match قبل از تسویه ERC-1155 تأیید می‌شود و این همان phantom-fill problem است که در chapter 12 پوشش داده می‌شود.

این دو باید همیشه به هم converge کنند. وقتی diverge می‌شوند، chain authoritative است. بoti که فقط به CLOB اعتماد کند drift می‌کند؛ botی که فقط به chain اعتماد کند آهسته معامله می‌کند. code production از هر دو استفاده می‌کند: CLOB برای تصمیم‌های حساس به سرعت، chain برای reconciliation دوره‌ای.

آدرس contract و ABI برای pUSD

pUSD stablecoin wrapperِ Polymarket است که از migration به V2 در سال 2025 استفاده می‌شود. contract در Polygon mainnet با آدرس 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB یک standard ERC-20 است.

سه read مهم برای یک bot:

  • balanceOf(proxy) - pUSD قابل‌استفاده شما. در هر restart آن را با viewی که CLOB از موجودی شما دارد compare کنید.
  • allowance(proxy, exchange_contract) - اینکه contractهای exchange مربوط به CTF/NegRisk می‌توانند pUSD شما را خرج کنند یا نه. برای order matching لازم است.
  • Transfer event subscription - deposit و withdrawal را بدون polling تشخیص می‌دهد.

USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) همچنان off-ramp pair باقی مانده است. بیشتر botها فقط به pUSD reads نیاز دارند؛ USDC فقط در چرخه‌های deposit/withdrawal اهمیت دارد.

Conditional Tokens Framework (CTF)

Outcome shareها توکن‌های ERC-1155 هستند که توسط Conditional Tokens Framework (CTF) گnosis mint می‌شوند. contract CTF در Polygon با آدرس 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 عرضه را برای هر position-id track می‌کند.

سه read:

  • balanceOf(proxy, position_id) - اینکه واقعاً چند outcome token برای آن market+outcome نگه می‌دارید.
  • getOutcomeSlotCount(condition_id) - تعداد outcomes (2 برای binary، N برای multi-outcome).
  • payoutNumerators, payoutDenominator - وقتی UMA بازار را resolve می‌کند set می‌شوند. با خواندن این‌ها قبل از اینکه UIِ CLOB به‌روزرسانی شود می‌فهمید کدام طرف برنده شده است.

position_id یک hash از (condition_id, outcome_index) است. آن را سمت client با helperِ getPositionId در CTF compute کنید یا math مربوط به keccak را در stack خود replicate کنید.

UMA Optimistic Oracle: proposed و disputed events

Optimistic Oracle (OO) از UMA همه dispute resolutionهای Polymarket را مدیریت می‌کند. دو event وجود دارد که bot شما ممکن است بخواهد subscribe کند.

  • ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice) - زمانی fired می‌شود که یک bot در Polymarket یک outcome را propose می‌کند.
  • DisputePrice(requester, identifier, timestamp, ancillaryData, disputer) - زمانی fired می‌شود که کسی outcome پیشنهادی را challenge می‌کند.

آدرس contract مربوط به OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. آن را با requester address مربوط به Polymarket filter کنید.

چرا subscribe کنیم: یک dispute یعنی resolution حالا contested شده و به یک vote در UMA طی 24 تا 72 ساعت نیاز دارد. در این بازه ممکن است بازار برای trading pause شود. botی که روی بازاری disputed position دارد باید فوراً مطلع شود.

خواندن Polygon event logs (web3.py / ethers)

دو implementation مرجع.

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

برای monitoring پیوسته از WebSocket transport (wss://...) به‌جای HTTP polling استفاده کنید - درخواست‌های کمتر و delivery سریع‌تر. بیشتر providerهای paid RPC در پلن‌های entry tier، WebSocket را هم ارائه می‌دهند.

چه زمانی on-chain بخوانیم و چه زمانی به API اعتماد کنیم

قواعد عملی از production.

  • به API اعتماد کنید برای: real-time order book، recent trades، pending orderهای خودتان، market metadata (slug، question، end date)، discovery رویداد/market.
  • به chain اعتماد کنید برای: pUSD balance خودتان، outcome-token inventory خودتان، verify کردن deposit و withdrawal، resolution outcomeها، وضعیت dispute.
  • هر دو را cross-check کنید برای: هر چیز مالی که bot آن را به‌عنوان fill ثبت کرده - event «matched» در API را با transfer مربوط به CTF در chain match کنید تا settlement تأیید شود.

قانون 5 ثانیه انتظار بعد از یک buy (chapter 12) همان realityِ on-chain است که وارد زمان API می‌شود. یک GTC sell که بلافاصله بعد از market buy ارسال شود، از chain check مقدار balance: 0 را می‌بیند، حتی اگر CLOB لحظاتی قبل match شده باشد.

code: تشخیص UMA dispute با استفاده از event subscription

Reference: disputeهای Polymarket مربوط به UMA را در real time monitor کنید.

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)

فیلد ancillaryData متن hex-encoded و شبیه JSON است که question بازار را در خود دارد. decode کردن آن به شما identifier معادل slug را می‌دهد تا با positionهای بازتان cross-reference کنید.

سوالات متداول

آدرس قرارداد pUSD Polymarket را از کجا پیدا کنم؟
pUSD در آدرس 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB روی Polygon قرار دارد. این توکن در 28 آوریل 2026 جایگزین USDC.e به‌عنوان collateral مرجع Polymarkets شد. لینک Polygonscan: https://polygonscan.com/token/0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB.
contract CTF در Polymarket چیست؟
CTF مخفف Conditional Tokens Framework است - contract مبتنی بر ERC-1155 که از Gnosis مشتق شده و outcome tokenها را صادر می‌کند (سهم‌های YES و NO که معامله می‌کنید). هر market در Polymarket یک CTF position است که منطق redemption آن به resolutionِ UMA گره خورده است. botها به‌ندرت لازم است مستقیماً با CTF کار کنند؛ SDK این کار را انجام می‌دهد.
چطور در Polygon به eventهای UMA dispute subscribe کنم؟
از طریق WebSocketِ providerهای Polygon RPC خود به eventهای "ProposePrice" و "DisputePrice" در UMA Optimistic Oracle V2 subscribe کنید. برای اینکه فقط disputeهای مربوط به Polymarket را بگیرید، فیلد requesterAddress (oracle adapter Polymarket) را filter کنید. نمونه codeها در chapter آمده‌اند.
اگر به Polymarket API اعتماد کنم، باز هم لازم است on-chain data بخوانم؟
برای بیشتر strategyها، نه. APIِ CLOB برای order book و trade data canonical است و gamma API برای metadata canonical است. وقتی on-chain می‌خوانید که (a) هشدارهای UMA dispute را سریع‌تر از زمانی که API نمایش می‌دهد لازم دارید، (b) می‌خواهید verify کنید یک deposit واقعاً رسیده است، یا (c) analytics سفارشی روی outcomeها/positionها می‌خواهید.
latency داده‌های on-chain Polygon در برابر Polymarket API چقدر است؟
زمان block در Polygon حدود 2 ثانیه است. Polymarket API معمولاً تغییرات order book را ظرف چندصد میلی‌ثانیه پس از match on-chain نمایش می‌دهد. برای بیشتر signalها، API از خواندن‌های on-chain خودتان سریع‌تر است. UMA disputeها استثنا هستند - event on-chain قبل از اینکه UI dispute را نشان دهد fire می‌شود.
آیا می‌توانم positionهای Polymarket را بدون CLOB API بخوانم؟
از نظر فنی بله - برای هر position، مقدار CTF balanceOf(walletAddress, positionId) را بخوانید. اما در عمل endpointِ CLOB API /trade/positions سریع‌تر است، pricing را هم شامل می‌شود و همه positionهای شما را یکجا aggregate می‌کند. فقط اگر به verify کردن نیاز دارید یا API down است، سراغ خواندن on-chain بروید.