[ERC4974] 評価システムをコントラクトで管理する仕組みを理解しよう！

# はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、評価システムをオンチェーン上で管理・運用する仕組みを提案している**ERC4974**についてまとめていきます！

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

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

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

# 概要

**ERC4974**は、Ethereumブロックチェーン上で数値による評価（Rating）を割り当て・管理する標準インターフェースを提案しています。
これにより、評価情報がスマートコントラクトに記録され、他のアプリケーションからも読み取れるようになります。

**ERC4974**の仕組みは、トークンやアカウントに対する「評価（スコア）」の概念をブロックチェーン上に導入することで、新たなユースケースを開拓する基盤となります。
例えば、評価に基づく投票権の分配やゲーム内の経験値、顧客へのロイヤリティポイント、保険のリスク判定など幅広い応用が可能です。

# 動機

これまでのブロックチェーンアプリケーションは、主にトークンの売買や資産管理に重点を置いてきました。
しかし、この「**資産中心モデル**」は、特にコミュニティベースのプロジェクトにおいて「課金プレイ（pay-to-play）」構造を生んでいました。
2021年には、多くのEVMゲームやDAOでこの傾向が問題視されました。

この課題に対処するために、**ERC4974**では評価という新しい要素を導入しています。
評価を使うことで、資産の取引以外にも多様なインセンティブ設計が可能になります。

具体的な活用例としては以下があります。

- DAOにおける投票重み付け

コミュニティへの貢献度などに応じてメンバーに評価を与え、その評価に基づいて投票権を調整することで、より健全なガバナンスを実現できます。

- 分散型ゲームでの経験値システム

プレイヤーの活動内容に応じて評価（経験値）を付与し、進行状況のトラッキングや新コンテンツのアンロックに活用できます。

- 顧客ロイヤリティプログラム

ビジネスに対する継続的な利用を評価に変換し、それに応じた特典や割引を提供できます。

- 分散型保険における資産リスク評価

保険対象資産に対してリスク評価を行い、その結果をもとに保険料やカバレッジ内容を調整することで、公平性と効率性を向上させられます。

**ERC4974**は、**ERC20**および**ERC721**などの既存トークン標準の構造や記述スタイルを参考に設計されています。
そのため、既存のスマートコントラクトとの互換性があります。

# 仕様

## 実装が必須なインターフェース

**ERC4974**に準拠する全てのコントラクトは、以下のインターフェースを実装する必要があります。

- **IERC4974**
- **IERC165**

:::note info
**ERC165**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/541184977a5cba51b759

## IERC4974インターフェース

```js:IERC4974.sol
// SPDX-License-Identifier: CC0

pragma solidity ^0.8.0;

/// @title EIP-4974 Ratings
/// @dev See https://eips.ethereum.org/EIPS/EIP-4974
///  Note: the EIP-165 identifier for this interface is #######.
///  Must initialize contracts with an `operator` address that is not `address(0)`.
interface IERC4974 /* is ERC165 */ {

    /// @dev Emits when operator changes.
    ///  MUST emit when `operator` changes by any mechanism.
    ///  MUST ONLY emit by `setOperator`.
    event NewOperator(address indexed _operator);

    /// @dev Emits when operator issues a rating. 
    ///  MUST emit when rating is assigned by any mechanism.
    ///  MUST ONLY emit by `rate`.
    event Rating(address _rated, int8 _rating);

    /// @dev Emits when operator removes a rating. 
    ///  MUST emit when rating is removed by any mechanism.
    ///  MUST ONLY emit by `remove`.
    event Removal(address _removed);

    /// @notice Appoint operator authority.
    /// @dev MUST throw unless `msg.sender` is `operator`.
    ///  MUST throw if `operator` address is either already current `operator`
    ///  or is the zero address.
    ///  MUST emit an `Appointment` event.
    /// @param _operator New operator of the smart contract.
    function setOperator(address _operator) external;

    /// @notice Rate an address.
    ///  MUST emit a Rating event with each successful call.
    /// @param _rated Address to be rated.
    /// @param _rating Total EXP tokens to reallocate.
    function rate(address _rated, int8 _rating) external;

    /// @notice Remove a rating from an address.
    ///  MUST emit a Remove event with each successful call.
    /// @param _removed Address to be removed.
    function removeRating(address _removed) external;

    /// @notice Return a rated address' rating.
    /// @dev MUST register each time `Rating` emits.
    ///  SHOULD throw for queries about the zero address.
    /// @param _rated An address for whom to query rating.
    /// @return int8 The rating assigned.
    function ratingOf(address _rated) external view returns (int8);
}

interface IERC165 {
    /// @notice Query if a contract implements an interface.
    /// @dev Interface identification is specified in EIP-165. This function
    ///  uses less than 30,000 gas.
    /// @param interfaceID The interface identifier, as specified in EIP-165.
    /// @return bool `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise.
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
```

### イベント

#### `NewOperator`

評価を管理する「オペレーター（operator）」が変更された時に発行されるイベント。
`setOperator`関数からのみ発行されます。

#### `Rating`

オペレーターが特定のアドレスに対して評価を行った時に発行されるイベント。
`rate`関数で評価が設定された時にのみ発行されます。

#### `Removal`

既存の評価が削除された時に発行されるイベント。
`removeRating`関数で評価が削除された時にのみ発行されます。

### 関数

#### `setOperator`

評価操作を行う権限を持つオペレーターを設定する関数。
呼び出し元（`msg.sender`）が現在のオペレーターである必要があります。
新たに指定するオペレーターが`address(0)`、もしくはすでに現在のオペレーターの場合はエラーを返します。
`NewOperator`イベントを発行します。

#### `rate`

指定されたアドレスに対して評価（数値）を設定する関数。
成功時に`Rating`イベントを発行します。
｀ゲームの経験値、DAOの評判スコア、信用ポイントなどに応用可能です。

#### `removeRating`

指定アドレスの評価を削除する関数。
成功時に`Removal`イベントを発行します。

#### `ratingOf`

指定されたアドレスの現在の評価値を返す関数。
`Rating`イベントが発行されるたびに、対応する状態更新が反映されている必要があります。
ゼロアドレス（`address(0)）が渡された場合はエラーを返します。

## IERC165インターフェース

#### `supportsInterface`

コントラクトが特定のインターフェース（例えば`IERC4974`）を実装しているかどう**ERC165**に基づき、30,000 gas未満で実行される必要があります。

# 補足

## 評価の付与

評価を付与する主体は、コントラクトオペレーター（operator）です。
このオペレーターは、例えばスポーツチームのコーチであったり、マルチシグウォレットを持つDAOであったり、柔軟に設定可能です。

**ERC4974**では、評価の意思決定プロセス（ガバナンス）そのものには干渉しません。
つまり、「**どうやって評価を決めるか**」は実装者に委ねられており、その柔軟性こそが多様なユースケースへの適用性を高めています。

また、**ERC4974**の設計は、評価を金銭的価値と結びつけることなく、「**コミュニティ内の評判**」として扱えるようにすることを意図しています。
これは、トークンのような金融資産とは異なる、非金銭的な信頼や貢献の見える化を目指した設計です。

## 評価値の型にint8を選んだ理由

- **符号付き整数であること（signed）**  
  
評価には「中立（0）」や「否定的（負の値）」なものも含めるべきであり、そのために符号付き整数を採用しています。
これにより、例えば悪質なユーザーやコントラクトへの警告的な評価が可能になります。

- **8ビットであること（8bit）**  

評価の範囲を意図的に狭めることで、比較や集計が容易になります。
あまりに大きな数値を許容すると、評価尺度のバラつきが大きくなり、標準化された意味を持たなくなる可能性があります。

## 評価の変更・削除の許容

評価は時間とともに変化するものであり、更新可能であるべきという考え方が提案されています。
例えば、以前は貢献度が高かったユーザーが後に不正を働いた場合、その評価は下げられるべきです。
また、評価者自身が「**自分の評価は正確でなかった**」と判断した場合に備え、評価の削除もできるようにするべきです。

この柔軟性により、評価システムが静的でなく、状況に応じて動的に調整されることが可能となります。

## インターフェースの検出方法

**ERC4974**に準拠コントラクトがサポートするインターフェースを外部から識別できるようにするために、**ERC165**の標準インターフェース検出機構を採用しています。
これにより、フロントエンドや他のコントラクトが、対象コントラクトが**ERC4974**に準拠しているかどうかを判定可能です。

## メタデータ設計

メタデータ拡張仕様では、以下の2つの関数を定義することが推奨されています。

- `name()`
    - コントラクト名を返す。
- `description()`
    - 評価システムの説明を返す。

これにより、ゲームや複数の評価基準をもつアプリケーションにおいて、ユーザーがどのような評価基準が使われているかを理解しやすくなります。

なお、空文字列（""）も有効な戻り値として認められているため、実装者がこの仕組みに反対する場合でも強制されるものではありません。

また、スマートコントラクトは任意の`name`や`description`を設定できるため、「**その評価システムが信頼できるものかかどうか**」はこの標準の範囲外であり、アプリケーション側で判断する必要があります。

## 潜在的な欠点

主観的な評価であるため正確性に欠ける場合があります。
これは避けられない性質ですが、更新や削除機能によりある程度対応可能です。
悪意ある評価の乱用によって、いじめや差別のような行為が発生する可能性もあるため、実装者は評価の透明性と健全性を保つ工夫が必要です。

# セキュリティ

## 偽の評価や誤解を招く評価のリスク

### 問題点

**ERC4974**によりブロックチェーン上で任意のアドレスに対して評価が付与されるため、悪意あるオペレーターが実在しない貢献を高く評価したり、無関係なアドレスに対して不当に低い評価をつける可能性があります。  
このような行為は以下のような被害を生む恐れがあります。

- DAOでの投票権重みに影響を与え、不正な意思決定を誘導する
- 一般ユーザーが不正確な評価に基づいて誤った選択をしてしまう

### 対策

このリスクに対して、**ERC4974**は以下の訂正メカニズムを提供しています。

- **評価の更新（rate）**
- **評価の削除（removeRating）**

これにより、評価が誤っていたり偏っていた場合でも、後から訂正できる仕組みが用意されています。

## オペレーター権限の乗っ取りリスク

### 問題点

**ERC4974**では、評価の付与・更新・削除といった全ての操作は「operator」と呼ばれる1つのアドレスによって制御されます。  
このアドレスが第三者に乗っ取られた場合、全ての評価が恣意的に操作されてシステムの信頼性が損なわれます。

### 対策

このリスクに対して、以下のような対策が推奨されています。

- **オペレーターアドレスの厳重な管理**
    - コールドウォレットなどによる保管
    - ハードウェアウォレットの利用
- **マルチシグによる制御**
    - 1人の個人ではなく、DAOや組織による複数署名で管理することで、不正アクセスのリスクを分散

## 全体的なセキュリティ設計の方針

**ERC4974**に準拠したコントラクトの安全性は、主に以下の2点に依存します。

- **operatorアドレスの安全な管理**
- **評価ルールの設計と運用**

つまり、コントラクト自体の仕様だけでなく、それをどう運用するかがセキュリティの鍵となります。
単一のoperatorが強力な権限を持つ設計であるため、その信頼性と透明性の担保が非常に重要です。
評価対象となるユーザーや開発者は、その評価の背後にある運用体制を必ず確認すべきです。

# 最後に

今回は「評価システムをオンチェーン上で管理・運用する仕組みを提案している**ERC4974**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4