Polymarket Bot 教程 · 第 9 章,共 32 章
直接读取 Polymarket 链上数据:USDC/pUSD 余额、用于查看 outcome supply 的 CTF 合约读数、UMA Optimistic Oracle 的 proposed/disputed 事件,以及 Polygon 交易日志-附代码。
本章内容
Polymarket 的 API 很方便,但最终一致。链上数据才是权威。本章会讲解生产环境 bot 用来校验自身账本的链上读取:pUSD 余额、outcome-token 库存、UMA dispute 事件,以及 CTF 合约状态。大多数生产 bot 最终都会采用的模式是:API 优先以追求速度,再定期进行链上对账以保证正确性。
- 链上有哪些内容(与 CLOB 相比)
- pUSD 合约地址和 ABI
- Conditional Tokens Framework(CTF)
- UMA Optimistic Oracle:proposed 和 disputed 事件
- 读取 Polygon event logs(web3.py / ethers)
- 何时读链上,何时信任 API
- 代码:通过事件订阅检测 UMA dispute
链上有哪些内容(与 CLOB 相比)
两个 state machine,两种事实。
链上(Polygon):pUSD 余额、outcome-token 库存(每个 token 的 ERC-1155 supply)、allowance 授权、UMA Optimistic Oracle 的提案和 dispute、deposit 和 withdrawal 事件。最终会正确;延迟大约是一个 Polygon block(约 2 秒)。
CLOB(Polymarket API):订单簿、最近成交、待处理限价单、撮合确认。实时但最终一致-在 ERC-1155 完成结算之前,撮合就已经被确认,这会产生第 12 章会讲到的 phantom-fill 问题。
两者应始终收敛。当它们不一致时,以链上为准。只信 CLOB 的 bot 会逐渐漂移;只信链上的 bot 会交易变慢。生产代码会同时使用两者:CLOB 负责对速度敏感的决策,链上负责周期性对账。
pUSD 合约地址和 ABI
pUSD 是 Polymarket 的抵押品代币,随 2026 年 4 月的 V2 平台升级推出。Polygon mainnet 上地址为 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB 的合约是标准 ERC-20(其链上 symbol 就是 “pUSD”,可在 Polygonscan 上核实)。
对 bot 来说,最重要的三个读取:
balanceOf(proxy)-你可支配的 pUSD。每次重启时都要与 CLOB 对你余额的视图进行对比。allowance(proxy, exchange_contract)-CTF/NegRisk 交易所合约是否可以花费你的 pUSD。撮合订单时需要。Transfer事件订阅-无需轮询即可检测 deposit 和 withdrawal。
USDC(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174)仍然是出入金配对资产。大多数 bot 只需要读取 pUSD;USDC 只在 deposit/withdrawal 周期中才重要。
Conditional Tokens Framework(CTF)
outcome shares 是由 Gnosis 的 Conditional Tokens Framework(CTF)铸造的 ERC-1155 token。Polygon 上位于 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 的 CTF 合约会跟踪每个 position-id 的 supply。
三个读取:
balanceOf(proxy, position_id)-你在该 market+outcome 下实际持有多少 outcome token。getOutcomeSlotCount(condition_id)-outcome 数量(二元市场为 2,多 outcome 市场为 N)。payoutNumerators、payoutDenominator-在 UMA 结算 market 时设置。读取这些值可以让你在 CLOB UI 更新前就知道哪一边获胜。
position_id 是(condition_id,outcome_index)的哈希。可通过 CTF 的 getPositionId helper 在客户端计算,或者在你的技术栈中复现 keccak 计算逻辑。
UMA Optimistic Oracle:proposed 和 disputed 事件
UMA 的 Optimistic Oracle(OO)负责所有 Polymarket dispute resolution。你的 bot 可能希望订阅两个事件。
ProposePrice(requester, identifier, timestamp, ancillaryData, proposer, proposedPrice)-当 Polymarket bot 提议一个 outcome 时触发。DisputePrice(requester, identifier, timestamp, ancillaryData, disputer)-当有人质疑该提议 outcome 时触发。
OO 合约地址:0xeE3Afe347D5C74317041E2618C49534dAf887c24。按 Polymarket 的 requester 地址进行过滤。
为什么要订阅:一旦发生 dispute,resolution 就变成争议状态,并需要 24-72 小时的 UMA 投票。在这段时间里,market 可能会暂停交易。持有该 disputed market 仓位的 bot 应该立即知晓。
读取 Polygon event logs(web3.py / ethers)
两个参考实现。
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 });
});
进行持续监控时,请使用 WebSocket 传输(wss://...)而不是 HTTP polling-请求更少,交付更快。大多数付费 RPC provider 都在入门套餐中包含 WebSocket。
何时读链上,何时信任 API
来自生产环境的实用规则。
- 信任 API 的场景:实时订单簿、最近成交、你自己的待处理订单、market 元数据(slug、question、end date)、event/market discovery。
- 信任链上 的场景:你自己的 pUSD 余额、你自己的 outcome-token 库存、deposit 和 withdrawal 验证、resolution 结果、dispute 状态。
- 同时交叉核对 的场景:任何被 bot 记为成交的金融数据-将 API 的 “matched” 事件与链上的 CTF transfer 对照,以确认结算。
买入后等待 5 秒的规则(第 12 章)体现的就是链上现实对 API 时间的侵入。刚提交市场买单后立即再提交 GTC 卖单,即使 CLOB 刚刚匹配成功,链上检查也会显示 balance: 0。
代码:通过事件订阅检测 UMA dispute
参考:实时监控与 Polymarket 相关的 UMA dispute。
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 字段是 hex 编码的类似 JSON 的文本,包含 market question。对其进行解码后,你就能得到类似 slug 的标识符,并将其与你持有的 open positions 进行交叉引用。












