原始数据

交易 hash: https://polygonscan.com/tx/0x0b597cb7a20fe2eeab1bf1b9266b9b10dbb10222b715b544a385fcd0dc8ef308

假设现在订单簿上报价:

rolesidetokenpriceamountyes-price
makerbuyno0.4150.59
makerbuyno0.4050.60

我挂 buy-yes 市价 $3.27 买入会发生什么?

最终链上成交记录:

rolesidetokenmaker_filledtaker_filledfee
makerbuyno2.0500005.0000000.000000
makerbuyno0.2133330.5333320.000000
takerbuyyes3.2699995.5333320.066870

撮合过程

  • 我的预算是 3.273.27,市价订单系统自己估算价格和数量
  • 首先 59C 一档全部吃下 3.270.595=0.323.27 - 0.59 * 5 = 0.32
  • 60C 吃下一部分, 0.32/0.600.5333330.32/0.60 \approx 0.533333 (保留 6 位小数)
  • 我的方向是 buy 对手方向是 sell,这个时候可以撮合成功的份额是 5+0.533333=5.5333335 + 0.533333 = 5.533333
  • 实际上少了 0.0000010.000001

对手方向不对称

但是因为 polymarket 订单簿是合并 Yes/No 之后的订单簿,我 buy yes 是可以和对手的 buy no 进行撮合。

处理规则如下:

  1. 我出多少钱,最多能铸造多少份额 Token
  2. 铸造这些 Token 对手需要付多少钱,抹零一次
  3. 对手抹零后的钱可以实际铸造多少 Token(实际撮合数量)
  4. 铸造这些 Token 对手需要花多少钱
  5. 通过对方花钱,反推我需要花多少钱,然后抹零(我方实际支持金额)
  6. 上一步抹零的钱转移到对手身上,重新计算对手花多少钱(对手实际支出金额)

对手的 buy-no 其实包含着一种 USDC=>Token 的转换,可以查看官方 SDK 订单构建的时候,会有两个参数:makerAmount/takerAmount。
合约就是输入 0.4150.41*5 USDC 换取 55 NO。这个比例记作 λ

λ=U/Tλ = U / T

若是我吃下 0.5333330.533333,交易的时候,我收到 0.5333330.533333 Yes Token,对手也会收到这么多 No Token,所以对手需要支付金额 U0U_{0}
因为我们合伙出钱把一个 1 x USDC => 1 x No + 1 x Yes

U0=0.5333330.40=0.21333320.213333U_{0} = 0.533333 * 0.40 = 0.2133332 \approx 0.213333

对手支付了 U0U_{0} 的 USDC 可以获取到 T0T_{0} 数量的 No Token。 因为

λ=U0/T0λ = U_{0} / T_{0}

转换一下就是:

T0=U0/λ=U0TUT_{0} = U_{0} / λ = \frac{U_{0} * T}{U}

代入数值计算

T0=0.5333325T_{0} = 0.5333325

以 maker 的视角进行计算,抹去小数点后六位,所以最终份额是 0.5333320.533332 不是 0.5333330.533333

0.5333320.533332 份额对手预计支付 0.5333320.400.533332 * 0.40,反推我预计支付,我这边抹零到对手身上。

实际结算不适用浮点数,使用整数,所以你会看到各种小数保留后六位。

上面实际是成交份额 533332533332,我预计支付的金额:

sum(533332)=T0U/T=5333320.6=319999.2sum (533332) = T_{0} * U/T = 533332 * 0.6 = 319999.2

但是后面 0.20.2 结算时候不能有这样的小数存在,所以在抹零换算一次换算到对手身上:

sum(1104165)=533332319999=213333sum (1104165) = 533332 - 319999 = 213333

手续费计算

手续费计算公式,CC 是交易份额,pp(1p)(1 - p)Yes/No 的价格,feeRatefeeRate 是收费比例,这里采用 0.050.05
收费比例不是固定的,根据市场有些是 0.040.04
手续费逐笔计费,最后合计,并且合计结果保留 5 位小数

fee=C×feeRate×p×(1p)fee = C × feeRate × p × (1 - p)

那么第一笔收费为:

fee=50.050.41(10.41)=0.060475fee = 5 * 0.05 * 0.41 * (1 - 0.41) = 0.060475

第二笔收费:

fee=0.5333320.050.40(10.40)=0.006399984fee = 0.533332 * 0.05 * 0.40 * (1 - 0.40) = 0.006399984

合并之后保留 5 位小数:

feet=0.060475+0.006399984=0.0668749840.06687fee_{t} = 0.060475 + 0.006399984 = 0.066874984 \approx 0.06687

其他

这次随便找了一个链上数据做校对,内容写的有点粗糙,大体上流程没有错误。
双方动作都是 Buy 的时候就是我和对手各自出钱铸造 Token,双方都是都是 Sell 的时候两边的 Token 合并成为 USDC,然后再分钱。