[ERC1202] オンチェーン投票の標準インターフェースの仕組みを理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、オンチェーン投票のための標準インターフェースを提案している規格である**ERC1202**についてまとめていきます！

:::note info
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-1202

:::note warn
**1202**は現在（2023年10月10日）では「**Draft**」段階です。
:::

# 概要

このEIPは、スマートコントラクトによる投票を実装するためのAPIです。
この標準は、投票機能、投票結果の表示機能、投票ステータスの設定機能を提供します。

# 動機

投票は、EVM（Ethereum Virtual Machine）プログラミングの初期から存在している概念で、DAO（分散型自治組織）や組織のガバナンスにおいて非常に重要な役割を果たしています
将来、多くのDAOがガバナンスの一環として投票を採用していくことになると考えています。

スマートコントラクトやトークンのための投票の標準を設けることには、以下の利点があります。

## 規格を持つことの利点

- **一般的なUIとアプリケーションの構築**
    - 標準化された投票プロトコルを使用することで、一般のユーザーが簡単に参加でき、標準化されたUIやアプリケーションが開発しやすくなります。
    - これにより、多くのユーザーが参加しやすくなり、分散型アプリケーション（DApp）やDAOのガバナンスが促進されます。
- **代理投票、スマートコントラクト投票、自動投票の実現**
    - 標準化された投票プロトコルは、代理投票、スマートコントラクトを介した自動投票などの高度な投票方法を可能にします。
    - これにより、効率的かつ信頼性の高い投票プロセスが実現できます。
- **チェーン上の投票結果の記録**
    - 投票結果を標準的な方法でブロックチェーンに記録できます。
    - これにより、DAOやDAppはプログラムによって投票結果を自動的に尊重できます。
- **トークン規格との互換性**
    - 標準化された投票プロトコルは、**ERC20**や**ERC721**などのトークン規格と連携しやすくなります。
    - これにより、トークンとの統合が簡単になり、多くの可能性が広がります。
- **エコシステムとの相互運用性**
    - 標準化により、Ethereumエコシステム内および他のシステムとの相互運用性が向上します。
    - これは異なるプラットフォーム間でのデータ共有や連携が容易になります。
- **投票のカスタマイズ**
    - 投票プロトコルをカスタマイズして、投票期限の設定や単一または複数の選択肢の決定が可能です。
    - ただし、これにはインターフェースの複雑性が伴うことに注意が必要です。
- **重み付けされた投票の記録**
    - 投票結果にトークンの数量に応じた重みを付けることができます。
    - これにより、トークン保有者の影響力を調整できます。
- **プライバシー保護と匿名投票の実現**
    - 投票者のアドレスと投票内容を関連付けない、ランダム化や難読化された投票オプションを提供することで、プライバシー保護型の投票や匿名投票が可能になるかもしれません。
- **報酬を通じた刺激**
    - 投票への参加や投票結果に応じた報酬を提供することで、ユーザーの投票への関与を奨励できます。

:::note info
投票の標準化により、分散型組織やプロジェクトはより効果的なガバナンスを行い、ユーザーに多くの利益をもたらす可能性があります。
:::

## 目標外／範囲外

この提案の非目標または対象外の項目は以下になります。

- **代理投票**
    - この提案では故意に代理投票を含めていません。
    - 代理投票に関する特定のユースケースを取り扱うためには、別のEIPが提案される可能性があります。
- **適格性や投票権の重み**
    - 一部の実装者は、投票権の重みや適格性を設定可能にしたいと考えています。
    - 例えば、OpenZeppelinの**GovernorBravo**の実装ではスナップショットを使用しています。
    - また、二次投票などの重み計算はこのEIPの範囲外です。
    - この提案は、既存の投票権計算方法と新しい方法の柔軟性を持つことを意図しています。
- **提案**
    - 提案についても故意に対象外としています。
    - 提案は`proposalId`で識別されますが、提案の詳細情報や、それらがブロックチェーン上で行われるかオフチェーンで行われるか、実行可能かどうかなど、提案に関する情報はこの提案から省略されています。
    - 提案に関する特定のユースケースを取り扱うためには、別のEIPが提案される可能性があります。
    - 関連する提案の一例として**ERC5247**が挙げられます。
- **署名集約 / 承認**
    - 実装コントラクトがユーザーにオフラインで投票または投票の承認を提出させ、他のアカウントがトランザクションを生成することを許可したい場合、署名の集約や承認はこの提案の範囲外です。
    - この特定のユースケースを取り扱うためには、別のEIPが提案される可能性があります。
    - 関連する提案の一例として**ERC5453**が挙げられます。

:::note info
これらの項目はこのEIPの範囲外であり、それぞれ別のEIPが提案されることで、これらの特定のユースケースに対処できるようになります。
:::

## ユースケース

このERCは、ウォレットに対してユーザーによるアクションが必要であることを示すための、極めてシンプルな機械可読（バイナリ）形式を導入するものです。
この形式は、将来のERCによって拡張可能であり、最大`64kB`のデータを含む任意のパラメーターを受け入れることができます。
例えば、取引所でトークンを承認する、HTTPリクエストを送信する、ある期間が経過した後にユーザーにキーの変更を要求するなど、ユーザーにアクションを促すための情報を提供します。
このERCは、将来の拡張性を考慮して設計されており、ウォレットやスマートコントラクトがユーザーアクションを適切に処理するための基盤を提供します。

このERCは、Ethereumの仮想マシンであるEVM（Ethereum Virtual Machine）プログラミングの初期の例の一つであり、また、分散型自治組織（DAO）や組織のガバナンスプロセスにおける鍵となる要素の一つです。
将来的には多くのDAOがガバナンスの一環として投票を利用する必要があります。
このため、スマートコントラクトやトークンの投票の標準を作成することには多くの利点があります。

この標準化によって、以下の利点を得ることができます。

- **統一性と予測性**
    - 投票標準を確立することで、異なるプロジェクトやDAO間での投票プロセスが統一され、予測可能になります。
    - これにより、投票の透明性と信頼性が向上し、参加者はより安心して参加できます。
- **スマートコントラクトの統合**
    - 投票標準はスマートコントラクトに統合され、スムーズな投票プロセスを提供します。
    - スマートコントラクトを介して行われる投票は、不正行為や誤解を減らし、信頼性を高めます。
- **効率的なガバナンス**
    - 投票標準を使用することで、DAOや組織のガバナンスプロセスが効率的になります。
    - 決定が効率的に行われ、意思決定プロセスが迅速化します。
- **拡張性**
    - 投票標準は将来のニーズに対応できるように設計されており、新しい機能や要件が追加される際に柔軟に対応できます。

:::note info
投票標準を確立することは、分散型組織やプロジェクトにとって重要であり、より効率的で透明性のあるガバナンスプロセスを実現するための一歩となります。
:::

# 仕様

この提案に準拠するコントラクトは、以下の**IERC1202Core**を実装する必要があります。

```javascript
interface IERC1202Core {
    event VoteCast(
        address indexed voter,
        uint256 indexed proposalId,
        uint8 support,
        uint256 weight,
        string reason,
        bytes extraParams
    );

    function castVote(
        uint256 proposalId,
        uint8 support,
        uint256 weight,
        string calldata reasonUri,
        bytes calldata extraParams
    ) external payable returns;

    function castVoteFrom(
        address from,
        uint256 proposalId,
        uint8 support,
        uint256 weight,
        string calldata reasonUri,
        bytes calldata extraParams
    ) external payable returns;

    function execute(uint256 proposalId, bytes memory extraParams) payable external;
}
```

### VoteCast

```javascript
event VoteCast(
    address indexed voter,
    uint256 indexed proposalId,
    uint8 support,
    uint256 weight,
    string reason,
    bytes extraParams
);
```

**概要**
投票が行われた時に発行され、投票の詳細情報を記録するイベント。

**詳細**
投票の情報をトランザクションログに記録し、ブロックチェーン上で公開されます。
これにより、投票に関する透明性とトレーサビリティが確保されます。以下は各パラメータの詳細です。

**パラメータ**
- `voter`
- 投票を行ったユーザーのアドレス。
- このアドレスに関連する投票を特定するためにインデックス化されます。
- `proposalId`
    - 投票対象の提案の一意の識別子。
    - 提案を特定するためにインデックス化されます。
- `support`
    - 投票の支持または反対を表す数値。
    - 通常、`1`は支持を、`0`は反対を示します。
- `weight`
    - 投票権の重み。
    - 投票者の重要性や影響力を示します。
- `reason`
    - 投票の理由やコメントを含むテキスト情報。
    - 投票の背後にある論理や説明を提供します。
- `extraParams`
    - 追加のパラメータやデータを格納するためのバイト配列。
    - 必要に応じてカスタムデータを含めることができます。

---

### castVote

```javascript
function castVote(
    uint256 proposalId,
    uint8 support,
    uint256 weight,
    string calldata reasonUri,
    bytes calldata extraParams
) external payable returns;
```

**概要**

投票を行う関数。
提案に対して投票を行い、その結果を記録します。

**詳細**

指定された提案に対して投票を行います。
投票の支持または反対は`support`パラメータで指定し、投票の重みは`weight`パラメータで指定します。
また、投票理由のURIは`reasonUri`パラメータで指定し、追加のパラメータは`extraParams`で渡すことができます。
投票が正常に行われた場合、関数は`payable`として宣言されているため、Etherも受け取ることができます。

**引数**

- `proposalId`
  - 投票対象の提案のIDを表す整数。
- `support`
  - 投票の支持（`1`）または反対（`0`）を表す整数。
- `weight`
  - 投票の重みを表す整数。
- `reasonUri`
  - 投票理由を示すURIを表す文字列。
- `extraParams`
  - 追加のパラメータを格納するバイト列。

---

### castVoteFrom

```javascript
function castVoteFrom(
    address from,
    uint256 proposalId,
    uint8 support,
    uint256 weight,
    string calldata reasonUri,
    bytes calldata extraParams
) external payable returns;
```

**概要**

別のアドレスから代理で投票を行う関数。
指定したアドレスからの投票を行い、その結果を記録します。

**詳細**

指定されたアドレス`from`から代理で投票を行います。
他のパラメータは`castVote`関数と同様に、投票の支持、重み、理由、および追加のパラメータを指定します。
また、Etherも受け取ることができます。

**引数**

- `from`
  - 投票を代理で行うアドレス。
- `proposalId`
  - 投票対象の提案のIDを表す整数。
- `support`
  - 投票の支持（`1`）または反対（`0`）を表す整数。
- `weight`
  - 投票の重みを表す整数。
- `reasonUri`
  - 投票理由を示すURIを表す文字列。
- `extraParams`
  - 追加のパラメータを格納するバイト列。

---

### execute

```javascript
function execute(uint256 proposalId, bytes memory extraParams) payable external;
```

**概要**

提案を実行する関数。
提案の実行をトリガーし、実行結果を取得します。

**詳細**

指定された提案を実行します。
提案のIDは`proposalId`パラメータで指定し、実行に必要な追加のパラメータは`extraParams`パラメータで指定します。
Etherも受け取ることができます。

**引数**

- `proposalId`
  - 実行対象の提案のIDを表す整数。
- `extraParams`
  - 実行に必要な追加のパラメータを格納するバイト列。

---

この提案に準拠するコントラクトは、**IERC1202MultiVote Interface**を実装することができます。
順位選択投票や変量投票など、複数の選択肢をサポートすることを意図している場合、 この提案に準拠するコントラクトは**IERC1202MultiVote Interface**を実装する必要があります。

```javascript
interface IERC1202MultiVote {
    event MultiVoteCast(
        address indexed voter,
        uint256 indexed proposalId,
        uint8[] support,
        uint256[] weight,
        string reason,
        bytes extraParams
    );

    function castMultiVote(
        uint256 proposalId,
        uint8[] support,
        uint256[] weight,
        string calldata reasonUri,
        bytes calldata extraParams
    ) external payable;
}
```
---

### MultiVoteCast

```javascript
event MultiVoteCast(
    address indexed voter,
    uint256 indexed proposalId,
    uint8[] support,
    uint256[] weight,
    string reason,
    bytes extraParams
);
```

**概要**

複数の投票がまとめて行われた時に発行されるイベント。

**詳細**

投票に関する情報を記録し、トランザクションの監視やデータの追跡に使用されます。

**パラメータ**

- `voter`
  - 投票者のアドレス。
- `proposalId`
  - 投票対象の提案のID。
- `support`
  - 投票の支持（`1`）または反対（`0`）を示す整数の配列。
- `weight`
  - 投票の重みを示す整数の配列。
- `reason`
  - 投票の理由を示す文字列。
- `extraParams`
  - 追加のパラメータを格納するバイト列。

---

### castMultiVote

```javascript
function castMultiVote(
    uint256 proposalId,
    uint8[] support,
    uint256[] weight,
    string calldata reasonUri,
    bytes calldata extraParams
) external payable;
```

**概要**

複数の投票をまとめて行う関数。
複数の投票情報を一度に提出し、それぞれの投票の結果を記録します。

**詳細**

この関数は、複数の投票情報を一括して提出できる関数です。
各投票情報は、対応する位置の要素同士が対応しており、複数の投票を一度に処理できます。
また、この関数は`payable`として宣言されているため、Etherも受け取ることができます。

**引数**

- `proposalId`
  - 投票対象の提案のIDを表す整数。
- `support`
  - 各投票の支持（`1`）または反対（`0`）を示す整数の配列。
- `weight`
  - 各投票の重みを示す整数の配列。
- `reasonUri`
  - 投票理由を示すURIを表す文字列。
- `extraParams`
  - 追加のパラメータを格納するバイト列。

---

## 情報を得る投票期間、投票資格、投票の重み

```javascript
interface IERC1202Info {
    function votingPeriodFor(uint256 proposalId) external view returns (uint256 startPointOfTime, uint256 endPointOfTime);
    function eligibleVotingWeight(uint256 proposalId, address voter) external view returns (uint256);
}
```

---

### votingPeriodFor

```javascript
function votingPeriodFor(uint256 proposalId) external view returns (uint256 startPointOfTime, uint256 endPointOfTime);
```

**概要**

指定された提案の投票期間の開始時点と終了時点を取得する関数。

**詳細**
指定した提案ID`proposalId`に関連する投票期間の詳細情報を取得できます。
具体的には、投票期間の開始時点と終了時点のタイムスタンプを返します。
これにより、投票期間の時間枠を知ることができます。

**引数**

- `proposalId`
  - 投票期間を取得したい提案のIDを表す整数。

**戻り値**

この関数は2つの整数値を返します：
- `startPointOfTime`
    - 投票期間の開始時点を表すタイムスタンプ。
- `endPointOfTime`
    - 投票期間の終了時点を表すタイムスタンプ。

---

### eligibleVotingWeight

```javascript
function eligibleVotingWeight(uint256 proposalId, address voter) external view returns (uint256);
```

**概要**

指定された提案に対する特定の投票者の投票権の重みを取得する関数。

**詳細**

指定した提案ID`proposalId`に対する特定の投票者の投票権の重みを取得できます。
投票権の重みは提案と投票者に依存し、提案ごとに異なります。
この情報は、投票者がどれだけの影響力を持つかを判断するのに役立ちます。

**引数**

- `proposalId`
  - 投票権の重みを取得したい提案のIDを表す整数。
- `voter`
  - 投票権の重みを取得したい投票者のアドレス。

**戻り値**

- `uint256`
  - 指定した提案に対する特定の投票者の投票権の重みを表す整数。

---

# 補足

以下にデザイン上の決断と根拠をまとめています。

## Granularity and Anonymity（粒度と匿名性）

この提案では、特定のアドレスからの投票を簡単に確認できるようにするために、「`ballotOf`」という主に閲覧（ビュー）機能を導入しました。
この機能は次の前提に基づいています。

- 特定のアドレスからの投票を直接確認できることが可能です。
ただし、実装者がこれを簡単に行いたくない場合、この機能へのすべての呼び出しを単純に拒否することができます。
この提案では、匿名投票と非匿名投票の両方をサポートすることを目指しています。
ただし、すべてのスマートコントラクトへの呼び出しはブロック履歴に記録されるため、暗号技術を使用しない限り、実際の秘密性は確保できません。暗号技術の詳細については、「**Second Feedback Questions 2018**」という関連するトピックを参照してください。

- 各個々のアドレスについて、1つの決定にのみ投票できると仮定しています。
ただし、利用可能な投票権をより細かい粒度で分散させることができます。
実装者がこれを許可する場合、ユーザーに新しいウォレットアドレスを作成し、その新しいアドレスに特定の権限を付与するよう要求することができます。
例えば、トークンベースの投票で投票権が投票者が保持するトークンの量に依存する場合、投票者はそのトークンを新しいアカウントに一部転送し、両方のアカウントから異なる選択肢に投票できます。

このアプローチにより、投票の精細な制御と匿名性を保持できます。
ただし、スマートコントラクトへの呼び出しは公開され、ブロックチェーン上で記録されることに留意する必要があります。

## 投票の重み

### 投票権の重み
この提案では、投票プロセスにおいて各個人の投票には異なる重み（影響力）が与えられることが前提とされています。
これは、異なる投票者が同じ提案に対して異なる影響力を持つことを意味します。
この重みは、投票者のトークンの保有量などの要因に基づいて計算され、各個人の投票が提案に対してどれだけの影響力を持つかを示します。

### 重みの設定
この重みは、実装者（コントラクトの開発者）によって設定されるか、コントラクトが作成される際に指定されます。
つまり、投票権の重みは、コントラクト内で定義されるルールに従って決定されます。
通常、この重みはトークンの保有量に基づいて計算されることが多いですが、他の要因に基づいても設定可能です。

### 重みの確認
各投票者は、自分のアドレス（`voter`）と特定の提案（`proposalId`）を指定して、「`eligibleVotingWeight`」などの関数を呼び出すことで、自分の投票権の重みを確認できます。
この機能を使用することで、各投票者は自分の重みを理解し、提案に対する影響力を透明かつ公平に把握できます。

この仕組みにより、投票プロセスがより公正で透明になり、異なる投票者が異なる重みを持つことができ、プロジェクトや組織の意思決定プロセスがより公平になります。

# 後方互換性

サポート・オプションは、**GoververnorBravo**との後方互換性を保つために`uint8`としました。
将来的には増やすことができます。

# セキュリティ考慮事項

### 他の契約との結合
投票標準は、トークンの分配、コンセンサスに基づいたアクションの実行、エンティティの代理またはマルチシグウォレットなど、他のコントラクトと組み合わせて使用されることが期待されています。
セキュリティを確保するために、標準インターフェースを使用して下流アクションを実行したり、上流からの入力（投票）を受けたりする必要があります。
将来の監査ツールもこの標準インターフェースに基づいていることが期待されています。

:::note info
**下流のアクション**

- **トークン分配**
投票が行われた後、特定の提案が受け入れられた場合、それに関連するトークンの分配が行われることがあります。
この分配は、投票の結果に基づいてトークンを受け取るアクションです。
例えば、プロジェクトの資金を決定するためにトークン保有者が投票し、投票結果に応じてトークンが分配されることがあります。

- **マルチシグウォレットの操作*
投票によって特定のアクションを実行するために、マルチシグウォレットを使用することがあります。
投票が成功した場合、マルチシグウォレットの所有者はそのアクションを実行することができます。
たとえば、プロジェクトの資金を移動するために必要なマルチシグウォレットの署名が投票に基づいて行われることがあります。

**上流のアクション**

- **投票**
上流のアクションとして、投票があります。
コミュニティメンバーやDAOメンバーが特定の提案に対して投票を行います。
投票はプロジェクトや組織の方針や意思決定に影響を与えるため、重要な上流のアクションです。

- **提案の作成**
また、提案の作成も上流のアクションです。
誰かが特定の提案を作成し、コミュニティやDAOメンバーに対して投票を促すことがあります。
提案は、新しいアクションを導入したり、プロジェクトの方向性を変更したりするための上流のアクションです。

下流のアクションは投票の結果に基づいて行われ、トークン分配やウォレット操作などが含まれます。
一方、上流のアクションは投票や提案の作成など、意思決定プロセスの一部として行われるアクションです。
投票プロトコルはこれらのアクションを効果的に結びつけ、透明かつ安全な意思決定プロセスを実現します。
:::

### 柔軟性と拡張性
このEIPは基本的な形式に保たれており、さまざまな実装バリエーションをサポートできるように拡張できることが期待されています。
これらのバリエーションは、アクションの動作や解釈に異なる仮定を含む可能性があります。
柔軟性を持つ投票のため、このEIPの拡張版が多く提案されることが期待されており、これらの拡張や実装は大規模なアプリケーションや高資産のボリュームに含まれる前に十分に監査されるべきです。

### 非トリビアル性
一部の投票アプリケーションでは、匿名性、ランダム性、時間ベースの締め切り、順序付けなどが前提条件とされていますが、これらの要件は実現が難しいことが知られています。
これらの要件を実装する場合、監査済みで実績のある共有ライブラリに依存することがお勧めされています。

:::note info
**トリビアル性（Non-triviality）**

特定の問題や要件が簡単に解決できるか、あるいは解決が難しいかを評価するための概念です。
一般的には、トリビアルな問題や要件は容易に理解し、解決できるものであり、非トリビアルな問題や要件は解決が難しく、複雑なものとされます。

この概念は、特にコンピュータサイエンスや数学の分野でよく使われます。
例えば、トリビアルな問題は簡単に計算できることが期待され、非トリビアルな問題は複雑で効率的な解決法が難しいことがあります。

具体的な例を挙げると、トリビアルな問題は`1 + 1`を計算することであり、簡単に解決できます。
一方、非トリビアルな問題は、大きな素数の因数分解や、NP完全な問題（多項式時間で解けるアルゴリズムが未知である問題）の解決などが含まれます。
これらの問題は、効率的な解決方法がまだ見つかっていないか、指数関数的な時間がかかることが知られています。

トリビアル性の評価は、問題の難易度や要求事項を理解し、適切な解決策を見つける時に重要です。
非トリビアルな問題は、新しいアプローチやアルゴリズムの開発が必要となることがあり、その解決には多くの研究と努力が必要です。
:::

### 潜在的な悪用
投票が標準化されコントラクトに組み込まれると、特定の方法で投票者に報酬を与えるコントラクトを作成する可能性があります。
これは贈賄や利益相反の悪用の可能性を生み出し、これまで難しかった問題を引き起こす可能性があります。

このように、投票プロトコルの標準化と使用にはセキュリティと検討すべき事項が存在し、これらに対処するための適切な対策が必要です。

# 引用

Zainan Victor Zhou (@xinbenlv), Evan (@evbots), Yin Xu (@yingogobot), "ERC-1202: Voting Interface [DRAFT]," Ethereum Improvement Proposals, no. 1202, July 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1202.

# 最後に

今回は「オンチェーン投票のための標準インターフェースを提案している規格である**ERC1202**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58