Tutorial Bot Polymarket · 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 2026. 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.


