Polymarket Bot Tutorial · 32개 챕터 중 9장

Polymarket on-chain data를 직접 읽어보세요: USDC/pUSD balances, outcome supply를 위한 CTF contract reads, UMA Optimistic Oracle의 proposed/disputed events, 그리고 Polygon transaction logs - 코드와 함께.

이 장에서 다루는 내용

Polymarket의 APIs는 편리하지만 eventually consistent합니다. 체인이 authoritative source입니다. 이 장에서는 production bot이 자체 bookkeeping을 검증하기 위해 사용하는 on-chain reads를 살펴봅니다: pUSD balances, outcome-token inventory, UMA dispute events, 그리고 CTF contract state. 대부분의 production bot이 수렴하는 패턴은 속도를 위한 API-first와 정확성을 위한 주기적인 on-chain reconciliation입니다.

  • on-chain에 있는 것들 (vs CLOB)
  • pUSD contract address와 ABI
  • Conditional Tokens Framework (CTF)
  • UMA Optimistic Oracle: proposed 및 disputed events
  • Polygon event logs 읽기 (web3.py / ethers)
  • 언제 on-chain을 읽고 언제 API를 신뢰할지
  • Code: event subscription으로 UMA dispute 감지

on-chain에 있는 것들 (vs CLOB)

두 개의 state machine, 두 개의 truth.

On-chain (Polygon): pUSD balances, outcome-token inventory (토큰별 ERC-1155 supply), allowance approvals, UMA Optimistic Oracle proposals와 disputes, deposit 및 withdrawal events. eventually correct하며, latency는 Polygon block 하나(약 2초)입니다.

CLOB (Polymarket API): order book, recent trades, pending limit orders, match acknowledgments. real-time이지만 eventually consistent합니다-match가 ERC-1155가 settle되기 전에 acknowledged되며, 이로 인해 12장에서 다루는 phantom-fill 문제가 발생합니다.

둘은 항상 수렴해야 합니다. 서로 다를 때는 체인이 authoritative합니다. CLOB만 신뢰하는 bot은 drift하게 되고, 체인만 신뢰하는 bot은 느리게 거래하게 됩니다. production code는 둘 다 사용합니다: 속도가 중요한 결정에는 CLOB를, 정확성을 위한 정기적 reconciliation에는 chain을 사용합니다.

pUSD contract address와 ABI

pUSD는 2025년 V2 migration 이후 사용되는 Polymarket의 stablecoin wrapper입니다. Polygon mainnet의 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB에 있는 contract는 표준 ERC-20입니다.

bot에 중요한 세 가지 read:

  • balanceOf(proxy)-실제로 쓸 수 있는 pUSD입니다. 모든 restart마다 CLOB가 보는 balance와 비교하세요.
  • allowance(proxy, exchange_contract)-CTF/NegRisk exchange contracts가 pUSD를 사용할 수 있는지 여부입니다. order matching에 필요합니다.
  • Transfer event subscription-polling 없이 deposit과 withdrawal을 감지합니다.

USDC(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174)는 여전히 off-ramp pair로 남아 있습니다. 대부분의 bot은 pUSD reads만 필요하고, USDC는 deposit/withdrawal cycle 동안에만 중요합니다.

Conditional Tokens Framework (CTF)

Outcome shares는 Gnosis의 Conditional Tokens Framework (CTF)가 mint하는 ERC-1155 tokens입니다. Polygon의 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045에 있는 CTF contract는 position-id별 supply를 추적합니다.

세 가지 read:

  • balanceOf(proxy, position_id)-해당 market+outcome에 대해 실제로 보유한 outcome tokens 수입니다.
  • getOutcomeSlotCount(condition_id)-outcome 수입니다(binary는 2, multi-outcome은 N).
  • payoutNumerators, payoutDenominator-UMA가 market을 resolve할 때 설정됩니다. 이를 읽으면 CLOB UI가 업데이트되기 전에 어떤 side가 이겼는지 알 수 있습니다.

position_id는 (condition_id, outcome_index)의 hash입니다. CTF의 getPositionId helper를 사용하거나, stack에서 keccak math를 직접 재현해 client-side로 계산하세요.

UMA Optimistic Oracle: proposed 및 disputed events

UMA의 Optimistic Oracle (OO)은 모든 Polymarket dispute resolution을 처리합니다. bot이 subscribe하고 싶을 수 있는 두 가지 event가 있습니다.

  • ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)-Polymarket bot이 outcome을 propose할 때 발생합니다.
  • DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)-누군가 proposed outcome에 이의를 제기할 때 발생합니다.

OO contract address: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Polymarket의 requester address로 필터링하세요.

왜 subscribe해야 하는가: dispute는 resolution이 이제 contested 상태이며 24-72시간의 UMA vote가 필요하다는 뜻입니다. 그 기간 동안 market은 거래가 paused될 수 있습니다. disputed market에 position을 보유한 bot은 즉시 알아야 합니다.

Polygon event logs 읽기 (web3.py / ethers)

두 가지 reference 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 });
});

지속적인 모니터링에는 HTTP polling 대신 WebSocket transport(wss://...)를 사용하세요-요청 수가 줄고 delivery가 더 빠릅니다. 대부분의 유료 RPC provider는 entry tier에서도 WebSocket을 포함합니다.

언제 on-chain을 읽고 언제 API를 신뢰할지

production에서 얻은 실용적인 규칙입니다.

  • API를 신뢰할 것: real-time order book, recent trades, 본인의 pending orders, market metadata(slug, question, end date), event/market discovery.
  • chain을 신뢰할 것: 본인의 pUSD balance, 본인의 outcome-token inventory, deposit 및 withdrawal 검증, resolution outcomes, dispute state.
  • 둘 다 교차 확인할 것: bot이 fill로 기록한 모든 금융 정보-API의 "matched" event와 chain의 CTF transfer를 대조해 settlement를 확인하세요.

구매 후 5초 기다리기 규칙(12장)은 API 시간에 on-chain 현실이 끼어드는 현상입니다. market buy 직후 submitted 된 GTC sell은 CLOB가 방금 matched했더라도 chain check에서 balance: 0을 보게 됩니다.

Code: event subscription으로 UMA dispute 감지

Reference: Polymarket 관련 UMA dispute를 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)

ancillaryData 필드는 market question이 들어 있는 hex-encoded JSON-ish text입니다. 이를 decode하면 open positions와 cross-reference할 수 있는 slug-equivalent identifier를 얻을 수 있습니다.

자주 묻는 질문

Polymarket pUSD contract는 어디에서 찾을 수 있나요?
pUSD는 Polygon의 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB에 있습니다. 2026년 4월 28일 USDC.e를 Polymarkets의 canonical collateral로 대체했습니다. Polygonscan 링크: https://polygonscan.com/token/0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB.
Polymarket의 CTF contract는 무엇인가요?
CTF는 Conditional Tokens Framework의 약자입니다 - outcome tokens(거래하는 YES와 NO shares)을 발행하는 Gnosis-derived ERC-1155 contract입니다. 각 Polymarket market은 UMA의 resolution과 연결된 redemption logic을 가진 CTF position입니다. bot은 대개 CTF와 직접 상호작용할 필요가 없으며, SDK가 이를 처리합니다.
Polygon에서 UMA dispute events를 어떻게 subscribe하나요?
Polygon RPC provider의 WebSocket을 통해 UMA Optimistic Oracle V2 contract의 "ProposePrice"와 "DisputePrice" events를 subscribe하세요. requesterAddress field(Polymarkets oracle adapter)로 필터링하면 Polymarket 관련 dispute만 가져올 수 있습니다. 코드 샘플은 이 장에 있습니다.
Polymarket API를 신뢰한다면 on-chain data를 읽어야 하나요?
대부분의 strategy에서는 아닙니다. CLOB API는 order book과 trade data의 canonical source이고, gamma API는 metadata의 canonical source입니다. on-chain reads는 (a) API에 나타나기 전에 더 빠른 UMA dispute alerts가 필요할 때, (b) deposit가 실제로 도착했는지 검증할 때, 또는 (c) outcomes/positions에 대한 custom analytics가 필요할 때 사용합니다.
on-chain Polygon data와 Polymarket API의 latency는 어느 정도인가요?
Polygon block time은 약 2초입니다. Polymarket API는 일반적으로 on-chain match 이후 수백 밀리초 내에 order book 변경을 보여줍니다. 대부분의 signal에서는 API가 자체 on-chain reads보다 더 빠릅니다. UMA disputes는 예외입니다 - on-chain event가 UI가 dispute를 반영하기 전에 발생합니다.
CLOB API 없이 Polymarket positions를 읽을 수 있나요?
기술적으로는 가능합니다 - 각 position에 대해 CTF balanceOf(walletAddress, positionId)를 읽으면 됩니다. 실무적으로는 CLOB API /trade/positions endpoint가 더 빠르고, pricing을 포함하며, 모든 positions를 집계합니다. verification이 필요하거나 API가 다운된 경우에만 on-chain reads로 돌아가세요.