Eric, Felix @DoraFactory

翻譯貢獻者: Yofu@DAOrayaki
原文 : Mitigate Quadratic Funding Inequality with a Progressive Tax System

二次方資助機制已經被許多主要的區塊鏈生態廣泛採用 : 以太坊、Filecoin、BSC、Solana、HECO、Flow 等。到目前爲止,二次方資助機制被證明可以有效分配公共資金,以支持開源開軟件項目 (Open Source Ventures) 和更通用的公共項目 (Public Goods)。

二次方資助機制允許人們進行多次投票,同時減少大戶的影響。在最近的 DoraHacks Austin Web3 黑客馬拉松中,二次方融資機制有效地限制了一次性大額捐贈的影響。(https://hackerlink.io/en/grant/Hack%20Austin/1

到目前爲止,主要的二次方融資資助平臺(HackerLink, Gitcoin, clr.fund)都使用了 Vitalik Buterin 的文章《Quadratic Payments》中描述的算法。對二次方資助的一箇中文的描述可以參考 Eric Zhang 的文章《二次方投票和二次方資助》(https://www.matataki.io/p/6113)。

該論文討論了二次方資助過程,並概述了女巫攻擊、共謀和選民激勵等問題。截至目前,二次方資助平臺一直在試圖解決女巫攻擊 (Sybil Attack) 和共謀 (Collusion) 的問題。HackerLink 正在新一版的二次方資助產品中使用 DoraID, DoraID 是來自 Dora Factory (DAO-as-a-Service 基礎設施)的身份協議證明,提供基於質押的身份服務。Gitcoin 剛剛發佈了其 GTC 治理代幣和反女巫 Staking 機制。clr.fund 實現了 MACI。所有這些做法都很有前景,使得二次方投票和二次方資助可以在更大的場景中使用。

然而,還有一個很少被現有的文章和平臺提及的問題,即匹配資金分配不平等的問題。

二次方資助中公共資金池分配的公平性問題

二次方資助在實踐中放大了不平等問題。擁有多數選票的項目將獲得更大比例的匹配池。在大多數情況下,這不僅會在分配資金時對早期項目造成不平等,還會抑制參與度(並有可能進一步鼓勵女巫攻擊,例如 Gitcoin GR9)。

在最近 HackerLink 上的 HECO Grant 中,排名第一的一個項目獲得了大量的選票(>HECO Grant Round-1 總票數的 70%),並在當時得到了總匹配資金池的 95%。經過一些調查(數據分析),大多數捐款並非來自女巫攻擊,而是來自於真實的投票。

在該項目以絕對優勢佔據排行榜後,HECO 的 Grant 在近兩週內幾乎沒有收到任何提交,因爲沒有人認爲自己可以與排名第一的項目競爭。最終,排名第一的項目團隊宣佈放棄不超過 50% 的匹配池配額,以支持本輪 Grant 中其他項目可以得到獎金池中的資助。最終,HECO Grant 決定將該項目的支持區(support area) 平均重新分配給所有獲得投票 >=10 的項目。這是一個一次性的解決方案,但它啓發了我們進一步解決不平等問題。

解決不平等問題最直接的機制是使用累進稅制 (Progressive Taxation)。

累進稅是第一次世界大戰之前的一個重要的發明。它在 20 世紀的戰後時期被廣泛採用,併成爲許多國家減少收入不平等的最重要機制之一。正如托馬斯•皮凱蒂(Thomas Piketty)在其著作中所指出的那樣,累進稅「提供了一種方法,其限制工業資本主義造成的不平等,同時保持對私人財產和競爭力量的尊重」。

DoraHacks 發起人 Eric Zhang: 用累進稅系統緩解二次方資助中的不平等問題一戰前和二戰後最高的收入所得稅稅率 (Source: One World in Data)

在二次方資助中,累進稅制的實施有多種方法。當然,最簡單的方法是設計一個靜態累進稅階梯。每一輪之後,每個項目都要繳納一定數額的稅款到一個公共資金池。然後,公共資金池就可以按照社區認爲公平的規則進行分配。

動態二次方累進稅算法

這裏,我們描述在每個 vote() 函數調用中動態重新分配財富的算法。

當項目獲得投票時,我們定義支持區稅率 $k$ 爲 :

DoraHacks 發起人 Eric Zhang: 用累進稅系統緩解二次方資助中的不平等問題

DoraHacks 發起人 Eric Zhang: 用累進稅系統緩解二次方資助中的不平等問題

在這種情況下,對支持區徵稅,就可以防止壟斷。我們可以對包含累進稅收的二次方資助機制進行模擬。可以看到,累進稅制平滑了公共資金的分配結果。在實際使用中,我們可以通過調節算法中的參數,改變稅收曲線的形狀,以達到公平性目標。

RULES ===================================

DoraHacks 發起人 Eric Zhang: 用累進稅系統緩解二次方資助中的不平等問題

用於模擬的 Javascript 代碼:

class Rule {
  constructor() {
    this.projects = []
    for (let i = 0; i < 10; i++) {
      this.projects[i] = {
        vote: 0,
        area: 0,
      }
    }

    this.area = 0
  }

  result() {
    for (let i = 0; i < this.projects.length; i++) {
      const p = this.projects[i]
      const s = (p.area / this.area * 10000).toFixed(2)

      const r = [this.area, p.area, p.vote]
      this.vote(i)
      const d = Number((p.area - r[1]).toFixed(2))
      const da = ((p.area / this.area * 10000) - Number(s)).toFixed(2)
      this.area = r[0]
      p.area = r[1]
      p.vote = r[2]

      console.log(`P${i + 1}: \t${r[2]} votes  \t${s} areas  \t${da} dA  \t${d} d`)
    }
  }
}

class OldRules extends Rule {
  vote(i) {
    const index = Math.min(i, this.projects.length - 1)
    const p = this.projects[index]
    this.area += p.vote
    p.area += p.vote
    p.vote += 1
  }
}

class Rules2 extends Rule {
  constructor() {
    super()
    this.top = 1
  }

  vote(i) {
    const index = Math.min(i, this.projects.length - 1)
    const p = this.projects[index]

    const k = Math.max(Math.min(1, (p.area - 5000) / this.top), 0)
      * p.area / Math.max(1, this.area)
    const added = p.vote * (1 - k)**2

    this.area += added
    p.area += added
    p.vote += 1

    if (p.area > this.top) {
      this.top = p.area
    }
  }
}

function test(rules, votes, ps) {
  for (let k = 0; k < votes.length; k++) {
    const v = votes[k]
    const p = ps[k]
    for (let n = 0; n < v; n++) {
      let i = 0
      while (p[i] && Math.random() > p[i]) {
        i++
      }
      rules.forEach(r => {
        r.vote(i)
      })
    }
  }
  rules.forEach((r, i) => {
    console.log(`RULES ${i + 1} ===================================`)
    r.result()
    console.log('')
  })
}

const AVERAGE = [1/10, 1/9, 1/8, 1/7, 1/6, 1/5, 1/4, 1/3, 1/2]
const BSC = [2/7, 1.8/6, 1.6/5, 1.4/4, 1.2/3, 1/2, 1/2, 1/2, 1/2]
const AMASS = new Array(9).fill(1/2)
const AAMASS = [0.76, ...new Array(8).fill(0.6)]
const BAMASS = [0.9, ...new Array(8).fill(0.6)]
const ATTACK = [0.01, 0.9, ...new Array(7).fill(1/2)]

test([
  new OldRules(),
  new Rules2(),
], [40000, 2000], [AAMASS, ATTACK])