Polymarket Bot Tutorial · Capitolo 9 di 32
Leggi direttamente i dati on-chain di Polymarket: saldi USDC/pUSD, letture del contract CTF per la supply degli outcome, eventi proposed/disputed di UMA Optimistic Oracle e transaction log di Polygon - con codice.
Cosa copre questo capitolo
Le API di Polymarket sono comode ma eventualmente consistent. La chain è la fonte autorevole. Questo capitolo illustra le letture on-chain che un bot in produzione usa per verificare la propria contabilità: saldi pUSD, inventory di outcome token, eventi di disputa UMA e stato del contract CTF. Il pattern verso cui convergono la maggior parte dei bot in produzione è API-first per la velocità, più reconciliation on-chain periodica per la correttezza.
Questo è il capitolo 9 della nostra serie in 32 parti sulla costruzione di un Polymarket trading bot. Trattiamo l'argomento in profondità nelle sezioni qui sotto. Il contenuto del corpo per ogni sezione viene scritto e pubblicato capitolo per capitolo; le risposte della FAQ e i riferimenti sono già completi e riflettono l'esperienza in produzione maturata gestendo il nostro trader.
- Cosa vive on-chain (vs in CLOB]
- Indirizzo contract pUSD e ABI
- Conditional Tokens Framework (CTF)
- UMA Optimistic Oracle: eventi proposed e disputed
- Lettura dei Polygon event log (web3.py / ethers)
- Quando leggere on-chain vs fidarsi dell'API
- Codice: rilevare una disputa UMA tramite event subscription
Cosa vive on-chain (vs in CLOB]
Due state machine, due verità.
On-chain (Polygon): saldi pUSD, inventory di outcome token (supply ERC-1155 per token), approval di allowance, proposizioni e dispute di UMA Optimistic Oracle, eventi di deposito e prelievo. Corretto in modo eventuale; la latenza è di un blocco Polygon (~2 secondi).
CLOB (Polymarket API): order book, trade recenti, pending limit order, acknowledgment dei match. Real-time ma eventualmente consistent — un match viene acknowledged prima che l'ERC-1155 si assesti, generando il problema del phantom-fill trattato nel capitolo 12.
I due devono sempre convergere. Quando divergono, la chain è la fonte autorevole. Un bot che si fida solo del CLOB andrà fuori sync; un bot che si fida solo della chain traderà lentamente. Il codice di produzione usa entrambi: CLOB per decisioni critiche in velocità e chain per reconciliation periodica.
Indirizzo contract pUSD e ABI
pUSD è il wrapper stablecoin di Polymarket usato dalla migrazione V2 del 2025. Il contract su Polygon mainnet all'indirizzo 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB è un ERC-20 standard.
Tre letture importanti per un bot:
balanceOf(proxy)— il tuo pUSD spendibile. Confrontalo con la view del CLOB sul tuo saldo a ogni riavvio.allowance(proxy, exchange_contract)— se i contract CTF/NegRisk exchange possono spendere il tuo pUSD. Richiesto per il matching degli ordini.- Subscription all'evento
Transfer— rileva depositi e prelievi senza polling.
USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) rimane la coppia off-ramp. La maggior parte dei bot ha bisogno solo di letture pUSD; USDC serve solo durante i cicli di deposito/prelievo.
Conditional Tokens Framework (CTF)
Le quote di outcome sono token ERC-1155 mintati dal Conditional Tokens Framework (CTF) di Gnosis. Il contract CTF su Polygon all'indirizzo 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 traccia la supply per position-id.
Tre letture:
balanceOf(proxy, position_id)— quanti outcome token possiedi davvero per quel market+outcome.getOutcomeSlotCount(condition_id)— numero di outcome (2 per binary, N per multi-outcome).payoutNumerators,payoutDenominator— impostati quando UMA risolve il market. Leggerli ti dice quale lato ha vinto prima che la UI del CLOB si aggiorni.
Il position_id è un hash di (condition_id, outcome_index). Calcolalo lato client tramite l'helper getPositionId del CTF oppure replica la keccak math nel tuo stack.
UMA Optimistic Oracle: eventi proposed e disputed
L'Optimistic Oracle (OO) di UMA gestisce tutta la dispute resolution di Polymarket. Due eventi a cui il tuo bot potrebbe voler iscriversi.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)— emesso quando un bot di Polymarket propone un outcome.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)— emesso quando qualcuno contesta l'outcome proposto.
Indirizzo contract OO: 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filtra per l'indirizzo requester di Polymarket.
Perché iscriversi: una disputa significa che la resolution è ora contestata e richiederà un voto UMA di 24-72 ore. Durante quella finestra, il market può essere sospeso dal trading. Un bot che detiene posizioni su un market disputato dovrebbe saperlo immediatamente.
Lettura dei Polygon event log (web3.py / ethers)
Due implementazioni di riferimento.
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 });
});
Per il monitoraggio continuo usa WebSocket transport (wss://...) invece del polling HTTP — meno request e consegna più rapida. La maggior parte dei provider RPC a pagamento include WebSocket già nei piani entry-level.
Quando leggere on-chain vs fidarsi dell'API
Regole pratiche dalla produzione.
- Fidati dell'API per: order book real-time, trade recenti, i tuoi pending order, metadata del market (slug, question, end date), discovery di eventi/market.
- Fidati della chain per: il tuo saldo pUSD, il tuo inventory di outcome token, verifica di depositi e prelievi, esiti di resolution, stato delle dispute.
- Confronta entrambi per: qualsiasi dato finanziario che il bot ha registrato come fill — abbina l'evento "matched" dell'API con il transfer CTF della chain per confermare il settlement.
La regola dei 5 secondi di attesa dopo un buy (capitolo 12) è la realtà on-chain che irrompe nel tempo dell'API. Un sell GTC inviato subito dopo un market buy vedrà balance: 0 dal controllo on-chain anche se il CLOB ha matched pochi istanti prima.
Codice: rilevare una disputa UMA tramite event subscription
Riferimento: monitorare in tempo reale le dispute UMA relative a Polymarket.
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)
Il campo ancillaryData è testo in stile JSON codificato in hex che contiene la market question. Decodificarlo ti dà l'identificatore equivalente allo slug da cross-referenziare con le tue posizioni aperte.











