PMWallets
EN

Polymarket 的 token ID、条件 ID 与 slug:它们怎么对应

一个 Polymarket 市场有给人看的 slug、给合约用的条件 ID,以及每个结果一个用于交易的 token ID。不同数据源说的是这三者中不同的一个,把它们对上,是任何 Polymarket 工具首先要做对的事。

三种标识

slug 是 polymarket.com 网址里给人看的名字;一个事件(比如一场选举)有自己的 slug,下面有一个或多个市场,每个市场也有 slug。条件 ID 是一个 32 字节的哈希,在条件代币合约里标识这个市场。市场的每个结果是一个代币,有一个 256 位的 token ID —— 也就是 CTF 的仓位 ID —— 订单簿和链上交易的就是它。

各自出现在哪里

Polymarket 的 Gamma API 描述市场和事件:slug、问题、conditionId、outcomes 和 clobTokenIds,还有 negRisk 之类的标记和费率设置。Data API 的成交和持仓带 token ID(字段名 asset)、conditionId 和 slug。CLOB 订单簿和它的 WebSocket 以 token ID 为键。链上成交只带 token ID。

怎样映射

从 slug 出发,向 Gamma 查市场,读出 conditionId 和 clobTokenIds。从日志里看到的 token ID 出发,交易所合约的 TokenRegistered 事件给出这一对 token ID 和它们的条件 ID,CTF 合约可以再核实;也可以用 clob_token_ids 参数查 Gamma 的 markets 接口。两个怪癖:outcomes 和 clobTokenIds 返回的是需要再解析一次的 JSON 字符串,而且两者顺序一致 —— 第一个 token 对应第一个结果。

python
import json, requests

m = requests.get("https://gamma-api.polymarket.com/markets",
                 params={"slug": "<market-slug>"}).json()[0]
outcomes  = json.loads(m["outcomes"])        # e.g. ["Yes", "No"]   — a JSON string, not a list
token_ids = json.loads(m["clobTokenIds"])    # same order as outcomes — also a JSON string
print(m["conditionId"], dict(zip(outcomes, token_ids)))

JavaScript 的坑

token ID 最长 78 位。JavaScript 的数字只在 2^53 以内是精确的,所以对原始 token ID 做 JSON.parse 会悄悄把它舍入成另一个数。token ID 要保持为字符串,需要运算时用 BigInt —— clobTokenIds 是字符串反而帮了忙,因为它的元素本来就是字符串。

当一个 token 没有市场

新建的市场可能在所有索引追上之前就开始交易,也有少数 token 永远没有公开的市场名称。工具应该标明一笔成交「未映射」,而不是丢掉或乱猜。PMWallets 通过三个来源映射每个 token —— 交易所的注册事件、我们回填的注册记录,以及 CTF 的条件数据 —— 如果还有超过一小部分成交额没映射上,榜单就暂缓发布。

常见问题

怎样找到一个 Polymarket 市场的 token ID?

用 slug 向 Polymarket 的 Gamma API 查这个市场,解析 clobTokenIds —— 一个 JSON 编码的列表,顺序与 outcomes 相同。每个元素就是对应结果的 token ID。

条件 ID 和 token ID 有什么区别?

条件 ID 在条件代币合约里标识这个市场;token ID 标识其中一个结果,交易的是它。一个二元市场有一个条件 ID 和两个 token ID。

为什么 Polymarket 的 token ID 在 JavaScript 里一解析就变了?

因为它们比 JavaScript 数字能精确表示的最大整数还大,会被舍入。请保持为字符串或 BigInt。

相关页面