web3-polymarket

Par elophanto · elophanto

Intégration Polymarket pour le trading sur les marchés de prédiction via Polygon. Couvre l'authentification (L1 EIP-712, L2 HMAC-SHA256, headers builder), le placement d'ordres (GTC/GTD/FOK/FAK, batch, post-only, heartbeat), les données de marché (Gamma API, Data API, carnet d'ordres, subgraph), le streaming WebSocket (canaux market/user/sports), les opérations CTF (split, merge, redeem, negative risk), le bridge (dépôts, retraits, multi-chain) et les transactions via relayer sans gas. À utiliser lors de la conception d'agents IA, de market makers autonomes, d'interfaces de marchés de prédiction, ou de toute application s'intégrant à Polymarket sur Polygon.

npx skills add https://github.com/elophanto/elophanto --skill web3-polymarket

Skill Polymarket

Source : Skill Polymarket officiel depuis Polymarket/agent-skills. À tenir à jour avec le dépôt source. La section « EloPhanto Setup » ci-dessous est locale — tout le reste reflète le dépôt source.

EloPhanto Setup

Déclencheurs : « polymarket », « prediction market », « place a bet », « polygon », « CLOB », « trading bot for polymarket ».

1. Installer le SDK Python à la première utilisation

pip install py-clob-client

(À exécuter via shell_execute. Pas dans pyproject.toml par défaut — installé uniquement quand ce skill est réellement utilisé.)

2. Stocker les identifiants dans le vault (une seule fois)

vault_set polymarket_private_key VALUE   # Clé privée EOA Polygon (portefeuille de financement)
vault_set polymarket_funder_address VALUE # Adresse proxy/safe depuis polymarket.com/settings (uniquement si signature_type != 0)
vault_set polymarket_signature_type 2    # 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE (le plus courant = 2)

3. Utiliser les identifiants en Python via vault_lookup

from py_clob_client.client import ClobClient

pk = vault_lookup("polymarket_private_key")
funder = vault_lookup("polymarket_funder_address")
sig_type = int(vault_lookup("polymarket_signature_type") or "2")

temp = ClobClient("https://clob.polymarket.com", key=pk, chain_id=137)
creds = temp.create_or_derive_api_creds()
client = ClobClient(
    "https://clob.polymarket.com",
    key=pk, chain_id=137,
    creds=creds,
    signature_type=sig_type,
    funder=funder,
)

3a. Détecter automatiquement quel signature_type détient la garantie

La même polymarket_private_key peut avoir des fonds répartis sur plusieurs types de proxy (EOA, POLY_PROXY, GNOSIS_SAFE). L'interface web Polymarket affiche le portefeuille auquel vous êtes connecté ; les utilisateurs déposent régulièrement dans un (p. ex. POLY_PROXY) tandis que leur configuration vault pointe vers un autre (p. ex. GNOSIS_SAFE). Résultat : le SDK indique $0 USDC et l'ordre échoue avec « insufficient balance » ou order_version_mismatch pour des raisons qui semblent être des erreurs de code.

Testez toujours les trois avant de passer le premier ordre d'une session et utilisez celui qui est financé :

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BalanceAllowanceParams, AssetType

best_sig_type = None
best_balance_usdc = 0.0
for sig_type in (0, 1, 2):
    try:
        c = ClobClient(
            "https://clob.polymarket.com",
            key=pk, chain_id=137,
            signature_type=sig_type,
            funder=funder,
        )
        c.set_api_creds(c.create_or_derive_api_creds())
        bal = c.get_balance_allowance(
            BalanceAllowanceParams(
                asset_type=AssetType.COLLATERAL, signature_type=sig_type
            )
        )
        usdc = int(bal.get("balance", 0)) / 1_000_000
        print(f"sig_type={sig_type}: ${usdc:.2f} USDC")
        if usdc > best_balance_usdc:
            best_balance_usdc, best_sig_type = usdc, sig_type
    except Exception as e:
        print(f"sig_type={sig_type}: probe failed ({e!r})")

if best_sig_type is None or best_balance_usdc == 0:
    raise SystemExit("No funded Polymarket wallet found for this key.")

# Utilisez le sig_type financé pour le reste de la session.
client = ClobClient(
    "https://clob.polymarket.com",
    key=pk, chain_id=137,
    signature_type=best_sig_type,
    funder=funder,
)
client.set_api_creds(client.create_or_derive_api_creds())

Si le best_sig_type détecté diffère du paramètre vault, mettez à jour le vault pour que les futures sessions ne re-testent pas : vault_set polymarket_signature_type <N>.

4. Placer les ordres UNIQUEMENT via l'API. Jamais via le navigateur.

Règle stricte : tout placement d'ordre Polymarket doit se faire via py-clob-client. Ne pilotez jamais polymarket.com via les outils de navigateur pour passer une transaction. L'interface web utilise des flux Privy/wallet intégré qui ne sont pas compatibles avec le portefeuille funder stocké dans le vault — les mauvais clics placent de vrais ordres, et l'agent n'a aucun moyen de vérifier l'ordre avant de le soumettre. Si le SDK échoue, arrêtez et rapportez l'erreur exacte ; ne basculez pas vers l'interface comme secours.

5. Découvrir tick_size et neg_risk dynamiquement (ne les codez pas en dur)

create_order exige les deux options. Les deux sont par marché et les valeurs SDK / on-chain font autorité — deviner un mauvais tick_size (« 0,01 » vs « 0,001 ») rejette l'ordre avec une erreur confuse.

# Récupérer directement depuis le CLOB
tick_size = client.get_tick_size(token_id)        # "0.001" / "0.01" / "0.1"
neg_risk  = client.get_neg_risk(token_id)         # bool

signed = client.create_order(
    OrderArgs(token_id=token_id, price=price, size=size, side=BUY),
    options={"tick_size": tick_size, "neg_risk": neg_risk},   # ← arg nommé, dict
)
resp = client.post_order(signed, OrderType.GTC)

Si get_neg_risk n'est pas disponible dans votre version installée de py-clob-client, lisez negRisk depuis les métadonnées du marché via gamma-api :

import requests
m = requests.get(
    "https://gamma-api.polymarket.com/events",
    params={"slug": EVENT_SLUG}, timeout=20,
).json()[0]["markets"]
neg_risk = next(mk for mk in m if mk["clobTokenIds"] and token_id in mk["clobTokenIds"])["negRisk"]

Le paramètre options est nommé uniquement dans les versions SDK plus récentes — le passer positivement lève TypeError. Utilisez toujours options=.

6. Garde-fous

  • Toujours confirmer avec le propriétaire avant de placer des ordres impliquant de l'argent réel. Traitez l'exécution de transactions comme un niveau de permission DESTRUCTIVE — présentez les paramètres d'ordre (token, côté, prix, taille, coût USDC) et attendez une approbation explicite.
  • Les opérations en lecture seule (orderbook, données de marché, positions) ne nécessitent aucune approbation.
  • Les transactions Polymarket utilisent USDC.e sur Polygon. Assurez-vous que le portefeuille funder dispose d'USDC.e et d'une petite quantité de POL pour le gaz (uniquement si signature_type=0 ; sans gaz via Gnosis Safe ne nécessite pas de POL).
  • Ne loggez jamais et ne répétez jamais polymarket_private_key.

Quand utiliser ce skill

Utilisez ce skill quand l'utilisateur demande ou doit construire :

  • Authentification API Polymarket (L1/L2, clés API, signature HMAC)
  • Placement ou gestion d'ordres (limite, marché, GTC, GTD, FOK, FAK, batch, annulation)
  • Lecture de données orderbook (prix, spreads, points médians, profondeur)
  • Récupération de données de marché (événements, marchés, par slug, par tag, pagination)
  • Souscriptions WebSocket (canal marché, canal utilisateur, sports)
  • Opérations CTF (split, merge, redeem positions)
  • Marchés à risque négatif (multi-résultat, conversion, risque négatif augmenté)
  • Opérations de bridge (dépôts, retraits, multi-chaîne)
  • Transactions sans gaz (client relayer, attribution d'ordre)
  • Intégration du programme builder (attribution d'ordre, clés API, tiers)
  • Utilisation du SDK Polymarket (TypeScript @polymarket/clob-client, Python py-clob-client)

Configuration API

API URL de base Authentification Objectif
CLOB https://clob.polymarket.com L2 pour endpoints de trading Orderbook, prix, soumission d'ordre
Gamma / Data https://gamma-api.polymarket.com Aucune Événements, marchés, recherche
Data API https://data-api.polymarket.com Aucune Transactions, positions, données utilisateur
WebSocket (Marché) wss://ws-subscriptions-clob.polymarket.com/ws/market Aucune Orderbook temps réel
WebSocket (Utilisateur) wss://ws-subscriptions-clob.polymarket.com/ws/user Creds API dans le message Mises à jour de transactions/ordres
WebSocket (Sports) wss://sports-api.polymarket.com/ws Aucune Scores en direct
Relayer https://relayer-v2.polymarket.com/ Headers builder Transactions sans gaz
Bridge https://bridge.polymarket.com Aucune Dépôts/retraits

Adresses de contrats (Polygon)

Contrat Adresse
USDC (USDC.e) 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
CTF (Conditional Tokens) 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045
CTF Exchange 0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E
Neg Risk CTF Exchange 0xC5d563A36AE78145C45a50134d48A1215220f80a
Neg Risk Adapter 0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296

Configuration client

TypeScript

import { ClobClient, Side, OrderType } from "@polymarket/clob-client";
import { Wallet } from "ethers"; // v5.8.0

const HOST = "https://clob.polymarket.com";
const CHAIN_ID = 137;
const signer = new Wallet(process.env.PRIVATE_KEY);

// Étape 1 : L1 — dériver les identifiants API
const tempClient = new ClobClient(HOST, CHAIN_ID, signer);
const apiCreds = await tempClient.createOrDeriveApiKey();

// Étape 2 : L2 — initialiser le client de trading
const client = new ClobClient(
  HOST,
  CHAIN_ID,
  signer,
  apiCreds,
  2,                // signatureType: 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE
  "FUNDER_ADDRESS"  // adresse du portefeuille proxy depuis polymarket.com/settings
);

Python

from py_clob_client.client import ClobClient
import os

host = "https://clob.polymarket.com"
chain_id = 137
pk = os.getenv("PRIVATE_KEY")

# Étape 1 : L1 — dériver les identifiants API
temp_client = ClobClient(host, key=pk, chain_id=chain_id)
api_creds = temp_client.create_or_derive_api_creds()

# Étape 2 : L2 — initialiser le client de trading
client = ClobClient(
    host,
    key=pk,
    chain_id=chain_id,
    creds=api_creds,
    signature_type=2,  # 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE
    funder="FUNDER_ADDRESS",
)

Référence rapide : Types d'ordres

Type Comportement Cas d'usage
GTC Reste sur le carnet jusqu'au remplissage ou annulation Ordres limite par défaut
GTD Actif jusqu'à expiration (secondes UTC). Min = now + 60 + N Auto-expire avant événements
FOK Remplit entièrement immédiatement ou annule Ordres marché tout ou rien
FAK Remplit ce qui est disponible, annule le reste Ordres marché partiellement remplissables
  • FOK/FAK BUY : amount = montant en dollars à dépenser
  • FOK/FAK SELL : amount = nombre de parts à vendre
  • Post-only : GTC/GTD uniquement — rejeté s'il croiserait le spread

Référence rapide : Types de signature

Type Valeur Description
EOA 0 Portefeuille Ethereum standard (MetaMask). Le funder est l'adresse EOA et devra avoir du POL pour le gaz.
POLY_PROXY 1 Portefeuille proxy personnalisé pour les utilisateurs Magic Link email/Google qui ont exporté la clé privée depuis Polymarket.com.
GNOSIS_SAFE 2 Portefeuille proxy multisig Gnosis Safe (le plus courant). À utiliser pour tout utilisateur nouveau ou récurrent.

Motif de base : Placer un ordre

TypeScript

const response = await client.createAndPostOrder(
  {
    tokenID: "TOKEN_ID",
    price: 0.50,
    size: 10,
    side: Side.BUY,
  },
  {
    tickSize: "0.01",  // depuis client.getTickSize(tokenID) ou objet marché
    negRisk: false,    // depuis client.getNegRisk(tokenID) ou objet marché
  },
  OrderType.GTC
);
console.log(response.orderID, response.status);

Python

from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

response = client.create_and_post_order(
    OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY),
    options={"tick_size": "0.01", "neg_risk": False},
    order_type=OrderType.GTC,
)
print(response["orderID"], response["status"])

Motif de base : Lire l'orderbook

TypeScript

// Aucune authentification requise
const readClient = new ClobClient("https://clob.polymarket.com", 137);
const book = await readClient.getOrderBook("TOKEN_ID");
console.log("Best bid:", book.bids[0], "Best ask:", book.asks[0]);

const mid = await readClient.getMidpoint("TOKEN_ID");
const spread = await readClient.getSpread("TOKEN_ID");

Python

read_client = ClobClient("https://clob.polymarket.com", chain_id=137)
book = read_client.get_order_book("TOKEN_ID")
mid = read_client.get_midpoint("TOKEN_ID")
spread = read_client.get_spread("TOKEN_ID")

Motif de base : Souscrire via WebSocket

const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "market",
    assets_ids: ["TOKEN_ID"],
    custom_feature_enabled: true,
  }));
  // Envoyer PING tous les 10s pour garder la connexion active
  setInterval(() => ws.send("PING"), 10_000);
};

ws.onmessage = (event) => {
  if (event.data === "PONG") return;
  const msg = JSON.parse(event.data);
  // msg.event_type: "book" | "price_change" | "last_trade_price" | "tick_size_change" | "best_bid_ask" | "new_market" | "market_resolved"
};

Fichiers de référence (charger à la demande)

Lisez ces fichiers uniquement quand la tâche exige plus de détails sur un sujet spécifique :

  • Authentication (L1/L2, builder headers, lifecycle des identifiants) : authentication.md
  • Order patterns (GTC/GTD/FOK/FAK, tick sizes, cancel, heartbeat, errors) : order-patterns.md
  • Market data (Gamma API, Data API, CLOB orderbook, subgraph) : market-data.md
  • WebSocket (canaux market/user/sports, subscribe, heartbeat) : websocket.md
  • CTF operations (split, merge, redeem, neg risk, token IDs) : ctf-operations.md
  • Bridge (deposits, withdrawals, chaînes/tokens supportés, status) : bridge.md
  • Gasless transactions (relayer client, wallet deployment, builder setup) : gasless.md

Vérifier

  • Un appel RPC/SDK réel a été émis (mainnet, devnet ou validateur local) et le payload de réponse est capturé dans la transcription, pas juste paraphrasé
  • Chaque transaction a été simulée (simulateTransaction ou équivalent) avant toute étape de signature/envoi ; les logs de simulation sont joints
  • Pour toute transaction signée/envoyée, la signature résultante est enregistrée et confirmée on-chain (status retourné par getSignatureStatuses ou URL d'explorateur)
  • Le slippage, la priority-fee et les limits de compute-unit ont été définis explicitement avec des valeurs numériques concrètes, pas laissés aux défauts de la bibliothèque
  • Les adresses de compte, mints et IDs de programme utilisés dans la run correspondent aux adresses web3-polymarket documentées pour le cluster ciblé (pas de mélange mainnet/devnet)
  • Le chemin d'erreur a été exercé au moins une fois (solde insuffisant, oracle périmé, blockhash expiré, etc.) et la gestion d'erreur de l'agent a produit un message lisible pour l'humain

Skills similaires