[ERC7509] コントラクトにECS設計パターンを導入する仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、スマートコントラクト開発において、ECS（エンティティ・コンポーネント・システム）設計パターンを採用する時のセキュリティ対策と、データ整合性の保持方法についての仕組みを提案している**ERC7509**についてまとめていきます！

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

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

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

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

# 概要

この提案は、エンティティ・コンポーネント・システム（ECS）という仕組みを最小限に定義しています。
ECSでは、エンティティというユニークな識別子を使って、複数のコンポーネント（データ）に割り当て、その後システム（ロジック）を用いて処理します。
この提案では、スマートコントラクトでECSを使用するためのインターフェイス仕様を標準化し、ユーザーが複数のコントラクトアプリケーションを自由に組み合わせて管理できるようにするための基本機能セットを提供しています。

:::note info
ECSはゲーム開発などでよく使われる設計パターンですが、この提案はそれをスマートコントラクトの世界に適用しようとしています。
ここでいう「**エンティティ**」は、ただの識別子で、それ自体に機能はありません。
一方、「**コンポーネント**」はデータの塊で、エンティティに特定の属性や状態を与えます。
最後に、「**システム**」はこのデータを使って実際の処理を行います。

例えば、ブロックチェーン上で動くゲームを考えてみます。
プレイヤー、アイテム、敵などがエンティティとなります。
これらのエンティティに対して、位置、健康状態、攻撃力などのコンポーネントを割り当てることができます。
そして、ゲームのロジック（システム）がこれらのコンポーネントを読み取り、プレイヤーが移動したり、戦ったりするための処理を行います。
:::

この提案は、このようなECSをスマートコントラクトで簡単に使えるようにするための標準的な方法を定義するものです。
これにより、開発者は複雑なアプリケーションをより簡単に構築できるようになると期待されます。

# 動機

ECS（エンティティ・コンポーネント・システム）は、データと振る舞いを分離することでコードの再利用性を向上させる設計パターンです。
主にゲーム開発で使われますが、提案されたシステムはスマートコントラクトを用いて、このECSを簡易に実装し、不必要な複雑さを排除しつつ、コントラクトの相互作用に適した機能改善を行っています。
ここでは、コンポーネントとシステムを簡単かつ自由に組み合わせることが可能です。
スマートコントラクト開発者としてECSを採用することの利点を説明します。

### 単純な設計による利点
ECSはデータ（コンポーネント）と振る舞い（システム）を分離することで、デカップリング、カプセル化、モジュール化のシンプルな設計を採用します。
これにより、ゲームやアプリケーションのアーキテクチャ設計が容易になります。

:::note info
デカップリングとは、システムやプログラムの構成要素間の依存関係を最小限にする設計手法です。
これにより、各要素が独立して機能し、変更や更新が他の部分に与える影響が減少します。
デカップリングを行うことで、コードの再利用性が高まり、メンテナンスや拡張が容易になります。
また、システムのテストやデバッグがシンプルになり、全体としての開発効率が向上します。
:::

### 柔軟な構成能力
各エンティティは異なるコンポーネントを組み合わせることができ、新しいエンティティのデータを操作するための異なるシステムを定義することも可能です。
これにより、アプリケーションの機能拡張やカスタマイズが容易になります。

### 拡張性
ECSは拡張にも適しており、新しいコンポーネントやシステムを定義することで、二つのゲームやアプリケーションが相互作用することが可能になります。

### 機能追加やアップグレードの容易さ
データと振る舞いが分離されているため、新機能の追加やアップグレードが既存のデータに影響を与えることなく行えます。

### 管理の容易さ
アプリケーションが複数のコントラクトから構成される場合、ECSは各コントラクトの状態を効果的に管理するのに役立ちます。

### コンポーネントの再利用性
コンポーネントは再利用可能であり、コミュニティと共有することで他の開発者の開発効率を向上させることができます。

:::note info
この提案により、スマートコントラクトを用いたアプリケーション開発がより柔軟で再利用可能、そして管理しやすくなることが期待されます。
ECSを利用することで、より効率的で拡張可能なアプリケーションやゲームを開発することができるようになります。
:::

# 仕様

ECSベースのプログラムを構築する際の一般的なワークフローは、エンティティ、コンポーネント、システムという3つの主要な要素を使用して、アプリケーションの構造を定義します。
ここで、「**ワールドコントラクト**」はこれらの要素を包含し、管理する容器として機能します。
以下は、ECSベースのプログラムを構築する時のステップです。

1. **ワールドコントラクトの作成**
    - `IWorld`インターフェースを実装してワールドコントラクトを作成します。
    - このコントラクトは、エンティティ、コンポーネントコントラクト、システムコントラクトの関係を確立し、管理します。

2. **エンティティの作成**
    - ワールドコントラクトの`createEntity()`関数を呼び出してエンティティを作成します。
    - エンティティは、複数のコンポーネントを持つことができるユニークな識別子です。

3. **コンポーネントコントラクトの作成**
    - `IComponent`インターフェースを実装してコンポーネントコントラクトを作成します。
    - コンポーネントはデータの容器であり、エンティティに添付されます。

4. **コンポーネントの登録**
    - ワールドコントラクトの`registerComponent()`関数を呼び出して、コンポーネントコントラクトを登録します。

5. **エンティティへのコンポーネントの添付**
    - ワールドコントラクトの`addComponent()`関数を呼び出して、エンティティにコンポーネントを添付します。

6. **システムコントラクトの作成**
    - インターフェースの制約がないシステムコントラクトを作成します。
    - システムコントラクトでは、任意の関数を定義でき、これを用いてエンティティのデータ（コンポーネント）を動的に変更します。

7. **システムの登録**
    - ワールドコントラクトの`registerSystem()`関数を呼び出して、システムコントラクトを登録します。

8. **システムの実行**
    - 最後に、登録されたシステムを実行して、エンティティの状態を変更します。

これらのステップを踏むことで、エンティティ（オブジェクト）、コンポーネント（データ）、システム（ロジック）を組み合わせた柔軟で拡張性の高いアプリケーションを構築できます。
ワールドコントラクトはこれらの要素を統合し、アプリケーションの状態を効果的に管理します。

## `IWorld.sol`

```ts
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.0;

interface IWorld {
    /**
     * Create a new entity.
     * @dev The entity MUST be assigned a unique Id.
     * If the state of the entity is true, it means it is available, and if it is false, it means it is not available.
     * When the state of the entity is false, you cannot add or remove components for the entity.
     * @return New entity id.
     */
    function createEntity() external returns (uint256);

    /**
     * Does the entity exist in the world.
     * @param _entityId is the Id of the entity.
     * @return true exists, false does not exist.
     */
    function entityExists(uint256 _entityId) external view returns (bool);

    /**
     * Get the total number of entities in the world.
     * @return The total number of entities.
     */
    function getEntityCount() external view returns (uint256);

    /**
     * Set the state of an entity.
     * @dev Entity MUST exist.
     * @param _entityId is the Id of the entity.
     * @param _entityState is the state of the entity, true means available, false means unavailable.
     */
    function setEntityState(uint256 _entityId, bool _entityState) external;

    /**
     * Get the state of an entity.
     * @param _entityId Id of the entity.
     * @return The current state of the entity.
     */
    function getEntityState(uint256 _entityId) external view returns (bool);

    /**
     * Register a component to the world.
     * @dev A component MUST be registered with the world before it can be attached to an entity.
     * MUST NOT register the same component to the world repeatedly.
     * It SHOULD be checked that the contract address returned by world() of the component contract is the same as the current world contract.
     * The state of the component is true means it is available, and false means it is not available. When the component state is set to false, it cannot be attached to the entity.
     * @param _componentAddress is the contract address of the component.
     */
    function registerComponent(address _componentAddress) external;

    /**
     * Does the component exist in the world.
     * @param _componentAddress is the contract address of the component.
     * @return true exists, false does not exist.
     */
    function componentExists(address _componentAddress)
        external
        view
        returns (bool);

    /**
     * Get the contract addresses of all components registered in the world.
     * @return Array of contract addresses.
     */
    function getComponents() external view returns (address[] memory);

    /**
     * Set component state.
     * @dev Component MUST exist.
     * @param _componentAddress is the contract address of the component.
     * @param _componentState is the state of the component, true means available, false means unavailable.
     */
    function setComponentState(address _componentAddress, bool _componentState)
        external;

    /**
     * Get the state of a component.
     * @param _componentAddress is the contract address of the component.
     * @return true means available, false means unavailable.
     */
    function getComponentState(address _componentAddress)
        external
        view
        returns (bool);

    /**
     * Attach a component to the entity.
     * @dev Entity MUST be available.Component MUST be available.A component MUST NOT be added to an entity repeatedly.
     * @param _entityId is the Id of the entity.
     * @param _componentAddress is the address of the component to be attached.
     */
    function addComponent(uint256 _entityId, address _componentAddress)
        external;

    /**
     * Whether the entity has a component attached,
     * @dev Entity MUST exist.Component MUST be registered.
     * @param _entityId is the Id of the entity.
     * @param _componentAddress is the component address.
     * @return true is attached, false is not attached
     */
    function hasComponent(uint256 _entityId, address _componentAddress)
        external
        view
        returns (bool);

    /**
     * Remove a component from the entity.
     * @dev Entity MUST be available.The component MUST have been added to the entity before.
     * @param _entityId is the Id of the entity.
     * @param _componentAddress is the address of the component to be removed.
     */
    function removeComponent(uint256 _entityId, address _componentAddress)
        external;

    /**
     * Get the contract addresses of all components attached to the entity.
     * @dev Entity MUST exist.
     * @param _entityId is the Id of the entity.
     * @return An array of contract addresses of the components owned by this entity.
     */
    function getEntityComponents(uint256 _entityId)
        external
        view
        returns (address[] memory);

    /**
     * Register a system to the world.
     * @dev MUST NOT register the same system to the world repeatedly.The system state is true means available, false means unavailable.
     * @param _systemAddress is the contract address of the system.
     */
    function registerSystem(address _systemAddress) external;

    /**
     * Does the system exist in the world.
     * @param _systemAddress is the contract address of the system.
     * @return true exists, false does not exist.
     */
    function systemExists(address _systemAddress) external view returns (bool);

    /**
     * Get the contract addresses of all systems registered in the world.
     * @return Array of contract addresses.
     */
    function getSystems() external view returns (address[] memory);

    /**
     * Set the system State.
     * @dev System MUST exist.
     * @param _systemAddress is the contract address of the system.
     * @param _systemState is the state of the system.
     */
    function setSystemState(address _systemAddress, bool _systemState) external;

    /**
     * Get the state of a system.
     * @param _systemAddress is the contract address of the system.
     * @return The state of the system.
     */
    function getSystemState(address _systemAddress)
        external
        view
        returns (bool);
}
```

---

### `createEntity`

```javascript
function createEntity() external returns (uint256);
```

**概要**

新しいエンティティを作成する関数。

**詳細**

この関数は、一意のIDを持つ新しいエンティティを作成します。
エンティティの状態が`true`の場合、利用可能であり、`false`の場合、利用不可です。
エンティティの状態が`false`のときは、そのエンティティに対してコンポーネントを追加または削除することはできません。

**戻り値**

- `uint256`
    - 新しいエンティティのID。

---

### `entityExists`

```javascript
function entityExists(uint256 _entityId) external view returns (bool);
```

**概要**

エンティティがワールド内に存在するか確認する関数。

**詳細**

指定されたIDを持つエンティティがワールド内に存在するかどうかを確認します。
存在する場合は`true`を、存在しない場合は`false`を返します。

**引数**

- `_entityId`
    - 確認したいエンティティのID。

**戻り値**

- `bool`
    - エンティティの存在有無。

---

### `getEntityCount`

```javascript
function getEntityCount() external view returns (uint256);
```

**概要**

ワールド内のエンティティの総数を取得する関数。

**詳細**

ワールドに登録されているエンティティの総数を返します。

**戻り値**

- `uint256`
    - エンティティの総数。

---

### `setEntityState`

```javascript
function setEntityState(uint256 _entityId, bool _entityState) external;
```

**概要**

エンティティの状態を設定する関数。

**詳細**

指定されたIDのエンティティの状態を設定します。
エンティティは事前に存在していなければなりません。
`_entityState`が`true`の場合は利用可能、`false`の場合は利用不可となります。

**引数**

- `_entityId`
    - 状態を設定したいエンティティのID。
- `_entityState`
    - エンティティの新しい状態。

---

### `getEntityState`

```javascript
function getEntityState(uint256 _entityId) external view returns (bool);
```

**概要**

エンティティの状態を取得する関数。

**詳細**

指定されたIDのエンティティの現在の状態を取得します。
利用可能な状態は`true`、利用不可の状態は`false`で表されます。

**引数**

- `_entityId`
    - 状態を確認したいエンティティのID。

**戻り値**

- `bool`
    - エンティティの現在の状態。

---

### `registerComponent`

```javascript
function registerComponent(address _componentAddress) external;
```

**概要**

コンポーネントをワールドに登録する関数。

**詳細**

コンポーネントをエンティティに添付する前に、コンポーネントをワールドに登録する必要があります。
同じコンポーネントを繰り返し登録することはできません。
コンポーネントコントラクトの`world()`関数が返すコントラクトアドレスが、現在のワールドコントラクトのアドレスと同じであることを確認するべきです。
コンポーネントの状態が`true`の場合は利用可能、`false`の場合は利用不可です。
コンポーネントの状態が`false`の場合、エンティティに添付することはできません。

**引数**

- `_componentAddress`
    - 登録したいコンポーネントのコントラクトアドレス。

---

### `componentExists`

```javascript
function componentExists(address _componentAddress) external view returns (bool);
```

**概要**

コンポーネントがワールド内に存在するか確認する関数。

**詳細**

指定されたコンポーネントアドレスがワールド内に存在するかどうかを確認します。
存在する場合は`true`、存在しない場合は`false`を返します。

**引数**

- `_componentAddress`
    - 確認したいコンポーネントのコントラクトアドレス。

**戻り値**

- `bool`
    - コンポーネントの存在有無。

---

### `getComponents`

```javascript
function getComponents() external view returns (address[] memory);
```

**概要**

ワールドに登録されているすべてのコンポーネントのコントラクトアドレスを取得する関数。

**詳細**

ワールドに登録されているすべてのコンポーネントのコントラクトアドレスの配列を返します。

**戻り値**

- `address[] memory`
    - 登録されているすべてのコンポーネントのコントラクトアドレスの配列。

---

### `setComponentState`

```javascript
function setComponentState(address _componentAddress, bool _componentState) external;
```

**概要**

コンポーネントの状態を設定する関数。

**詳細**

指定されたコンポーネントアドレスのコンポーネントの状態を設定します。
コンポーネントは事前に存在していなければなりません。
`_componentState`が`true`の場合は利用可能、`false`の場合は利用不可となります。

**引数**

- `_componentAddress`
    - 状態を設定したいコンポーネントのコントラクトアドレス。
- `_componentState`
    - コンポーネントの新しい状態。

---

### `getComponentState`

```javascript
function getComponentState(address _componentAddress) external view returns (bool);
```

**概要**

コンポーネントの状態を取得する関数。

**詳細**

指定されたコンポーネントアドレスのコンポーネントの現在の状態を取得します。
利用可能な状態は`true`、利用不可の状態は`false`で表されます。

**引数**

- `_componentAddress`
    - 状態を確認したいコンポーネントのコントラクトアドレス。

**戻り値**

- `bool`
    - コンポーネントの現在の状態。

---

### `addComponent`

```javascript
function addComponent(uint256 _entityId, address _componentAddress) external;
```

**概要**

エンティティにコンポーネントを添付する関数。

**詳細**

指定されたIDのエンティティに、指定されたアドレスのコンポーネントを添付します。
エンティティは利用可能である必要があり、コンポーネントも利用可能でなければなりません。
同じコンポーネントをエンティティに繰り返し追加することはできません。

**引数**

- `_entityId`
    - コンポーネントを添付したいエンティティのID。
- `_componentAddress`
    - 添付したいコンポーネントのアドレス。

---

### `hasComponent`

```javascript
function hasComponent(uint256 _entityId, address _componentAddress) external view returns (bool);
```

**概要**

エンティティに特定のコンポーネントが添付されているか確認する関数。

**詳細**

指定されたIDのエンティティに、指定されたアドレスのコンポーネントが添付されているかどうかを確認します。
エンティティは存在していなければならず、コンポーネントは登録されていなければなりません。

**引数**

- `_entityId`
    - コンポーネントの存在を確認したいエンティティのID。
- `_componentAddress`
    - 確認したいコンポーネントのアドレス。

**戻り値**

- `bool`
    - コンポーネントが添付されている場合は`true`、そうでない場合は`false`。

---

### `removeComponent`

```javascript
function removeComponent(uint256 _entityId, address _componentAddress) external;
```

**概要**

エンティティからコンポーネントを削除する関数。

**詳細**

指定されたIDのエンティティから、指定されたアドレスのコンポーネントを削除します。
エンティティは利用可能であり、コンポーネントは以前にエンティティに追加されていなければなりません。

**引数**

- `_entityId`
    - コンポーネントを削除したいエンティティのID。
- `_componentAddress`
    - 削除したいコンポーネントのアドレス。

---

### `getEntityComponents`

```javascript
function getEntityComponents(uint256 _entityId) external view returns (address[] memory);
```

**概要**

エンティティに添付されているすべてのコンポーネントのアドレスを取得する関数。

**詳細**

指定されたIDのエンティティに添付されているすべてのコンポーネントのアドレスの配列を返します。
エンティティは存在していなければなりません。

**引数**

- `_entityId`
    - コンポーネントのアドレスを取得したいエンティティのID。

**戻り値**

- `address[] memory`
    - エンティティに添付されているすべてのコンポーネントのアドレスの配列。

---

### `registerSystem`

```javascript
function registerSystem(address _systemAddress) external;
```

**概要**

システムをワールドに登録する関数。

**詳細**

指定されたアドレスのシステムをワールドに登録します。
同じシステムを繰り返しワールドに登録することはできません。
システムの状態が`true`の場合は利用可能、`false`の場合は利用不可です。

**引数**

- `_systemAddress`
    - 登録したいシステムのコントラクトアドレス。

---

### `systemExists`

```javascript
function systemExists(address _systemAddress) external view returns (bool);
```

**概要**

システムがワールド内に存在するかどうかを確認する関数。

**詳細**

指定されたアドレスのシステムがワールド内に存在するかどうかを確認します。

**引数**

- `_systemAddress`
    - 確認したいシステムのコントラクトアドレス。

**戻り値**

- `bool`
    - システムが存在する場合は`true`、そうでない場合は`false`。

---

### `getSystems`

```javascript
function getSystems() external view returns (address[] memory);
```

**概要**

ワールドに登録されているすべてのシステムのコントラクトアドレスを取得する関数。

**詳細**

ワールドに登録されているすべてのシステムのコントラクトアドレスの配列を返します。

**戻り値**

- `address[] memory`
    - 登録されているすべてのシステムのコントラクトアドレスの配列。

---

### `setSystemState`

```javascript
function setSystemState(address _systemAddress, bool _systemState) external;
```

**概要**

システムの状態を設定する関数。

**詳細**

指定されたアドレスのシステムの状態を設定します。
システムは事前に存在していなければなりません。
`_systemState`が`true`の場合は利用可能、`false`の場合は利用不可となります。

**引数**

- `_systemAddress`
    - 状態を設定したいシステムのコントラクトアドレス。
- `_systemState`
    - システムの新しい状態。

---

### `getSystemState`

```javascript
function getSystemState(address _systemAddress) external view returns (bool);
```

**概要**

システムの状態を取得する関数。

**詳細**

指定されたアドレスのシステムの現在の状態を取得します。
利用可能な状態は`true`、利用不可の状態は`false`で表されます。

**引数**

- `_systemAddress`
    - 状態を確認したいシステムのコントラクトアドレス。

**戻り値**

- `bool`
    - システムの現在の状態。

## `IComponent.sol`

```ts
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.0;
import "./Types.sol";

interface IComponent {
    /**
     * The world contract address registered by the component.
     * @return world contract address.
     */
    function world() external view returns (address);

    /**
     *Get the data type and get() parameter type of the component
     * @dev SHOULD Import Types Library, which is an enumeration Library containing all data types.
     * Entity data can be stored according to the data type.
     * The get() parameter data type can be used to get entity data.
     * @return the data type array of the entity
     * @return get parameter data type array
     */
    function types()
        external
        view
        returns (Types.Type[] memory, Types.Type[] memory);

    /**
     *Store entity data.
     * @dev entity MUST be available. The system that operates on it MUST be available.
     * The entity has the component attached.
     * @param _entityId is the Id of the entity.
     * @param _data is the data to be stored.
     */
    function set(uint256 _entityId, bytes memory _data) external;

    /**
     *Get the data of the entity according to the entity Id.
     * @param _entityId is the Id of the entity.
     * @return Entity data.
     */
    function get(uint256 _entityId) external view returns (bytes memory);

    /** Get the data of the entity according to the entity Id and parameters.
     * @param _entityId is the Id of the entity.
     * @param _params is an extra parameter, it SHOULD depend on whether you need it.
     * @return Entity data.
     */
    function get(uint256 _entityId, bytes memory _params)
        external
        view
        returns (bytes memory);
}
```

---

### `world`

```javascript
function world() external view returns (address);
```

**概要**

コンポーネントによって登録されたワールドコントラクトのアドレスを取得する関数。

**詳細**

この関数は、コンポーネントが登録されたワールドコントラクトのアドレスを返します。
コンポーネントがどのワールドコントラクトに属しているかを知るために使用します。

**戻り値**

- `address`
    - ワールドコントラクトのアドレス。

---

### `types`

```javascript
function types() external view returns (Types.Type[] memory, Types.Type[] memory);
```

**概要**

エンティティのデータタイプとget()関数のパラメータタイプを取得する関数。

**詳細**

この関数は、Typesライブラリをインポートすることを前提としています。
Typesライブラリは、すべてのデータタイプを含む列挙型ライブラリです。
エンティティデータは、データタイプに従って格納されます。
`get()`関数のパラメータデータタイプは、エンティティデータを取得するために使用されます。

**戻り値**

- `Types.Type[] memory`
    - エンティティのデータタイプ配列。
- `Types.Type[] memory`
    - getパラメータのデータタイプ配列。

---

### `set`

```javascript
function set(uint256 _entityId, bytes memory _data) external;
```

**概要**

エンティティデータを保存する関数。

**詳細**

利用可能なエンティティのデータを保存します。
これを操作するシステムも利用可能である必要があります。
エンティティは指定されたコンポーネントを添付していなければなりません。

**引数**

- `_entityId`
    - データを保存したいエンティティのID。
- `_data`
    - 保存するデータ。

---

### `get`

```javascript
function get(uint256 _entityId) external view returns (bytes memory);
```

**概要**

エンティティIDに基づいてエンティティデータを取得する関数。

**詳細**

指定されたエンティティIDのエンティティデータを取得します。

**引数**

- `_entityId`
    - データを取得したいエンティティのID。

**戻り値**

- `bytes memory`
    - エンティティデータ。

---

### `get`

```javascript
function get(uint256 _entityId, bytes memory _params) external view returns (bytes memory);
```

**概要**

エンティティIDと追加パラメータに基づいてエンティティデータを取得する関数。

**詳細**

指定されたエンティティIDと追加パラメータを用いてエンティティデータを取得します。
追加パラメータは、必要に応じて使用します。

**引数**

- `_entityId`
    - データを取得したいエンティティのID。
- `_params`
    - 追加のパラメータ。

**戻り値**

- `bytes memory`
    - エンティティデータ。

## ライブラリ

[`Types.sol`](https://eips.ethereum.org/assets/eip-7509/Types.sol)ライブラリには、上記のインターフェイスで使用されるSolidity型の列挙が含まれています。

# 補足

### 型情報を単純なバイト配列の代わりに含める理由は何ですか？

型情報を含める主な理由は、コンポーネントを使用する際の型の正確性を保証し、潜在的なエラーや不整合を避けるためです。
外部開発者は、型に基づいて明確にデータの設定や取得を行うことができます。
これにより、データの予期せぬ扱いによるバグを減らすことが可能になります。

### 存在しないエンティティと状態が`false`のエンティティを区別する理由は何ですか？

エンティティの状態だけでは、そのエンティティが実際に存在するかどうかを判断することはできません。
外部の貢献者がエンティティに基づいてコンポーネントを作成する場合、使用するエンティティが存在しなければ、作成したコンポーネントは意味をなさない可能性があります。
コンポーネントの作成者は、まずエンティティが存在するかどうかを確認し、エンティティが存在する場合、そのエンティティの状態が`false`であっても意味があります。
なぜなら、エンティティの状態が`true`になるのを待ってからコンポーネントをエンティティに添付できるからです。

### `getEntityComponents`関数が全てのコンポーネントのIDではなく、アドレスを返す理由は何ですか？

`getEntityComponents`には、エンティティに添付されたすべてのコンポーネントのアドレスを直接返すデザインを採用しています。
これは、エンティティが多くのコンポーネントIDを含む可能性があるため、ユーザーが複数回にわたってコンポーネントのアドレスをリクエストする必要がある状況を避けるためです。

### `registerComponent`と`registerSystem`が外部からの権限を提供できるかどうか？

これは、アプリケーションやゲームのオープン性に依存します。
開発者の参加を奨励する場合、彼らが登録するコンポーネントやシステムの状態は`false`であるべきで、使用する前に彼らが悪意のあるコードを提出していないかを確認する必要があります。
その後、`setComponentState`や`setSystemState`を使ってそれらを有効化できます。

### コンポーネントで追加パラメータを持つ`get`を使用するタイミングは？

コンポーネントには2つの`get`関数があります。
1つはエンティティIDのみを渡す必要があるもので、もう1つはデータを取得するための追加パラメータとして`_params`を持つものです。
例えば、エンティティのレベルに対応するHPを保存するコンポーネントを定義した場合、そのレベルに合ったHPを取得したいときは、`_params`としてエンティティのレベルを持つ`get`関数を呼び出します。

# 実装

以下に実装コードが格納されています。

https://eips.ethereum.org/assets/eip-7509/

# セキュリティ

エンティティ・コンポーネント・システム（ECS）を実装する時のセキュリティと整合性の保持についての説明です。

### タイプ情報の重要性
- **目的**
    - コンポーネントを使用する時の型の正確さを保証し、潜在的なエラーや不整合を避けるため。
- **方法**
    - 外部開発者が型に基づいてデータを設定・取得できるように、単純なバイト配列の代わりに型情報を含めます。

### 存在しないエンティティと状態が`false`のエンティティの区別
- **理由**
    - エンティティの状態だけでは、そのエンティティが実際に存在するかどうかを判断できないため。
- **方法**
    - コンポーネントを作成する前に、エンティティが存在するかどうかを確認します。
    - 存在すれば、そのエンティティの状態がfalseであっても、コンポーネントをエンティティに添付する意味があります。

### `getEntityComponents`関数の設計
- **選択**
    - エンティティに添付されたすべてのコンポーネントのアドレスを返す設計を採用。
- **理由**
    - エンティティが多くのコンポーネントIDを含む可能性があり、ユーザーが複数回にわたってコンポーネントアドレスをリクエストする必要があるため。

### `registerComponent`と`registerSystem`の外部権限
- **方針**
    - アプリケーションやゲームの開放性に依存します。
    - 開発者の参加を奨励する場合、登録されたコンポーネントやシステムの状態は初期状態で`false`に設定し、使用前に悪意のあるコードが提出されていないかを確認する必要があります。

### コンポーネントコントラクト内での`set`関数の使用
- **指針**
    - エンティティを変更する他の関数を提供せず、`set`関数内でエンティティが利用可能であり、操作するシステムも利用可能であるかどうかをチェックします。

### システムの登録とセキュリティチェック
- **プロセス**
    - ワールドにシステムが登録されると、ワールド内のすべてのエンティティのコンポーネントデータを操作できるようになります。
    - そのため、ワールドに登録する前にすべてのシステムコントラクトのコードセキュリティをチェックし、監査する必要があります。

### 新バージョンでの非推奨の扱い
- **管理**
    - 新しいバージョンで非推奨となったエンティティ、コンポーネントコントラクト、システムコントラクトは、`setEntityState()`, `setComponentState()`, `setSystemState()`を使用してタイムリーに無効化する必要があります。

# 引用

Rickey (@HelloRickey), "ERC-7509: Entity Component System [DRAFT]," Ethereum Improvement Proposals, no. 7509, September 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7509.

# 最後に

今回は「スマートコントラクト開発において、ECS（エンティティ・コンポーネント・システム）設計パターンを採用する時のセキュリティ対策と、データ整合性の保持方法についての仕組みを提案している**ERC7509**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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