Polymarket Bot Tutorial · Chapitre 9 sur 32
Lisez directement les données on-chain de Polymarket : soldes USDC/pUSD, lectures du contrat CTF pour l'offre des outcomes, événements proposés/disputés de l'UMA Optimistic Oracle, et logs de transactions Polygon - avec du code.
Ce que couvre ce chapitre
Les APIs de Polymarket sont pratiques, mais eventualy consistent. La blockchain fait autorité. Ce chapitre passe en revue les lectures on-chain qu'un bot de production utilise pour vérifier sa propre comptabilité : soldes pUSD, inventaire des outcome tokens, événements de litige UMA et état du contrat CTF. Le schéma vers lequel convergent la plupart des bots de production est API-first pour la vitesse, plus une réconciliation on-chain périodique pour la fiabilité.
- Ce qui vit on-chain (vs in CLOB)
- Adresse du contrat pUSD et ABI
- Conditional Tokens Framework (CTF)
- UMA Optimistic Oracle : événements proposés et disputés
- Lecture des logs d'événements Polygon (web3.py / ethers)
- Quand lire on-chain vs faire confiance à l'API
- Code : détecter un litige UMA via subscription d'événements
Ce qui vit on-chain (vs in CLOB)
Deux state machines, deux vérités.
On-chain (Polygon) : soldes pUSD, inventaire des outcome tokens (supply ERC-1155 par token), approvals d'allowance, propositions et litiges UMA Optimistic Oracle, événements de dépôt et de retrait. Correct à terme ; la latence correspond à un bloc Polygon (~2 secondes).
CLOB (Polymarket API) : order book, trades récents, ordres limite en attente, acknowledgments de matching. Temps réel mais eventualy consistent - un match est acknowledged avant que l'ERC-1155 ne soit settled, ce qui crée le problème de phantom-fill traité au chapitre 12.
Les deux doivent toujours converger. Lorsqu'ils divergent, la chaîne fait autorité. Un bot qui fait confiance uniquement au CLOB dérivera ; un bot qui fait confiance uniquement à la chaîne sera lent. Le code de production utilise les deux : le CLOB pour les décisions critiques en termes de vitesse, la chaîne pour la réconciliation périodique.
Adresse du contrat pUSD et ABI
pUSD est le stablecoin wrapper de Polymarket utilisé depuis la migration V2 de 2025. Le contrat sur Polygon mainnet à l'adresse 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB est un standard ERC-20.
Trois lectures importantes pour un bot :
balanceOf(proxy)- votre pUSD dépensable. À comparer avec la vue du CLOB sur votre solde à chaque redémarrage.allowance(proxy, exchange_contract)- si les contrats d'échange CTF/NegRisk peuvent dépenser votre pUSD. Requis pour le matching des ordres.- subscription à l'événement
Transfer- détecte les dépôts et retraits sans polling.
L'USDC (0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) reste le pair de sortie. La plupart des bots n'ont besoin que des lectures pUSD ; l'USDC n'est utile que pendant les cycles de dépôt/retrait.
Conditional Tokens Framework (CTF)
Les outcome shares sont des tokens ERC-1155 mintés par le Conditional Tokens Framework (CTF) de Gnosis. Le contrat CTF sur Polygon à 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 suit la supply par position-id.
Trois lectures :
balanceOf(proxy, position_id)- combien d'outcome tokens vous détenez réellement pour ce market+outcome.getOutcomeSlotCount(condition_id)- nombre d'outcomes (2 pour binary, N pour multi-outcome).payoutNumerators,payoutDenominator- définis lorsque UMA resolve le market. Les lire vous indique quel côté a gagné avant la mise à jour de l'UI du CLOB.
Le position_id est un hash de (condition_id, outcome_index). Calculez-le côté client via l'helper getPositionId du CTF ou reproduisez la keccak math dans votre stack.
UMA Optimistic Oracle : événements proposés et disputés
L'Optimistic Oracle (OO) d'UMA gère toute la résolution des litiges de Polymarket. Deux événements auxquels votre bot peut vouloir s'abonner.
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)- déclenché lorsqu'un bot Polymarket propose un outcome.DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)- déclenché lorsque quelqu'un conteste l'outcome proposé.
Adresse du contrat OO : 0xeE3Afe347D5C74317041E2618C49534dAf887c24. Filtrez par l'adresse requester de Polymarket.
Pourquoi s'abonner : un litige signifie que la résolution est désormais contestée et nécessitera un vote UMA de 24 à 72 h. Pendant cette fenêtre, le market peut être mis en pause pour le trading. Un bot qui détient des positions sur un market disputé doit le savoir immédiatement.
Lecture des logs d'événements Polygon (web3.py / ethers)
Deux implémentations de référence.
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 });
});
Pour une surveillance continue, utilisez un transport WebSocket (wss://...) plutôt que du polling HTTP - moins de requêtes et une livraison plus rapide. La plupart des fournisseurs RPC payants incluent WebSocket dès les offres d'entrée de gamme.
Quand lire on-chain vs faire confiance à l'API
Règles pratiques issues de la production.
- Faites confiance à l'API pour : order book temps réel, trades récents, vos propres ordres en attente, metadata des markets (slug, question, date de fin), découverte des events/markets.
- Faites confiance à la chaîne pour : votre propre solde pUSD, votre propre inventaire d'outcome tokens, vérification des dépôts et retraits, résultats de résolution, état des litiges.
- Recoupez les deux pour : tout élément financier que le bot a enregistré comme fill - faites correspondre l'événement "matched" de l'API avec le transfert CTF de la chaîne pour confirmer le settlement.
La règle d'attente de 5 secondes après un buy (chapitre 12) correspond à la réalité on-chain qui s'impose au temps de l'API. Un sell GTC soumis immédiatement après un market buy verra balance: 0 lors du contrôle chaîne, même si le CLOB a matched quelques instants plus tôt.
Code : détecter un litige UMA via subscription d'événements
Référence : surveiller en temps réel les litiges UMA liés à 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)
Le champ ancillaryData est un texte hex-encodé de type JSON contenant la question du market. Le décoder vous donne l'identifiant équivalent au slug à recouper avec vos positions ouvertes.












