ERC-6551: Non-fungible Token Bound Accounts（翻訳）

# はじめに（Introduction）

以下を[Google翻訳](https://translate.google.com/)などで翻訳します。

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

# ERC-6551: Non-fungible Token Bound Accounts  

NFTトークンが所有するスマートコントラクトアカウントのインターフェイスとレジストリ

**著者**
Jayden Windle (@jaydenwindle), Benny Giang <bg@futureprimitive.xyz>, Steve Jang, Druzy Downs (@druzydowns), Raymond Huynh (@huynhr), Alanah Lam <alanah@futureprimitive.xyz>, Wilkins Chung (@wwhchung) <wilkins@manifold.xyz>, Paul Sullivan (@sullivph) <paul.sullivan@manifold.xyz>, Auryn Macmillan (@auryn-macmillan), Jan-Felix Schwarz (@jfschwarz), Anton Bukov (@k06a), Mikhail Melnik (@ZumZoom), Josh Weintraub (@jhweintraub) <josh@revest.finance>, Rob Montgomery (@RobAnon) <rob@revest.finance>, vectorized (@vectorized)

**Authors** 2023-02-23
**Requires** [EIP-165](https://eips.ethereum.org/EIPS/eip-165), [EIP-721](https://eips.ethereum.org/EIPS/eip-721), [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167), [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)

:::note warn
[この EIP はピアレビュー中です。 この EIP に興味がある場合は、このディスカッション リンクを使用して参加してください。](https://ethereum-magicians.org/t/non-fungible-token-bound-accounts/13030)
:::

## 要旨（Abstract）

この提案は、すべてのNFTトークンにイーサリアム アカウントを割り当てるシステムを定義します。
これらのトークン バインド アカウントにより、NFT は既存のスマート コントラクトやインフラストラクチャに変更を加えることなく、資産を所有し、アプリケーションと対話できるようになります。

## 動機（Motivation）

[ERC-721](https://eips.ethereum.org/EIPS/eip-721) 標準により、代替不可能なトークン アプリケーションの爆発的な増加が可能になりました。
いくつかの注目すべきユースケースには、繁殖可能な猫、生成可能なアートワーク、取引所の流動性ポジションなどがあります。

ただし、NFT はエージェントとして機能したり、他のオンチェーン資産と関連付けたりすることはできません。
この制限により、現実世界の多くの非代替資産を NFT として表すことが困難になります。
例えば：

- ロールプレイング ゲームのキャラクターで、実行したアクションに基づいて時間の経過とともに資産や能力が蓄積されます
- 多くの代替可能コンポーネントと代替不可能なコンポーネントで構成される自動車
- 複数の代替資産で構成される投資ポートフォリオ
- 施設へのアクセスを許可し、過去の交流履歴を記録するパンチパス会員カード

この提案は、すべてのNFTにイーサリアムユーザーと同じ権利を与えることを目的としています。
これには、資産の自己保管、任意の操作の実行、複数の独立したアカウントの制御、複数のチェーンにわたるアカウントの使用の機能が含まれます。
そうすることで、この提案は、イーサリアムの既存の所有権モデルを反映する共通のパターンを使用して、複雑な現実世界の資産を NFT として表すことができるようになります。

これは、既存および将来のすべての NFT に一意で決定的なスマート コントラクト アカウント アドレスを割り当てるシングルトン レジストリを定義することによって実現されます。
各アカウントは単一の NFT に永続的にバインドされ、アカウントの制御はその NFT の所有者に付与されます。

この提案で定義されたパターンでは、既存の NFT スマート コントラクトを変更する必要はありません。
また、オンチェーン プロトコルからオフチェーン インデクサーに至るまで、イーサリアム アカウントをサポートするほぼすべての既存のインフラストラクチャとすぐに使用できる互換性があります。
トークン バインドされたアカウントは、既存のすべてのオンチェーン資産標準と互換性があり、将来作成される新しい資産標準をサポートするように拡張できます。

この提案は、すべての NFT にイーサリアム アカウントの全機能を提供することで、既存および将来の NFT の多くの新しいユースケースを可能にします。

## 仕様（Specification）

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.

## 概要（Overview）

この提案で概説されているシステムには、次の 2 つの主要コンポーネントがあります。

- トークンバインドされたアカウント用のシングルトンレジストリ
- トークンバインドされたアカウント実装のための共通インターフェース

次の図は、NFT、NFT 保有者、トークン バインドされたアカウント、レジストリの関係を示しています。

![diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29914/b6a53026-1570-3f06-2644-8912680c79d9.png)

## レジストリ（Registry）

レジストリは、すべてのトークン バインドされたアカウント アドレス クエリのエントリ ポイントとして機能するシングルトン コントラクトです。
これには 2 つの機能があります。

- `createAccount` - `implementation` アドレスを指定して、NFT のトークン バインド アカウントを作成します
- `account` - `implementation` アドレスを指定して、NFTのトークンバインドされたアカウントアドレスを計算します

The registry is permissionless, immutable, and has no owner. 
The complete source code for the registry can be found in the Registry Implementation section. 
The registry MUST be deployed at address 0x000000006551c19487814612e58FE06813775758 using Nick’s Factory (0x4e59b44847b379578588920cA78FbF26c0B4956C) with salt 0x0000000000000000000000000000000000000000fd8eb4e1dca713016c518e31.
レジストリは権限がなく、不変であり、所有者がありません。
レジストリの完全なソース コードは、「レジストリの実装」セクションにあります。
レジストリは、Nick’s Factory (`0x4e59b44847b379578588920cA78FbF26c0B4956C`) とソルト `0x0000000000000000000000000000000000000000fd8eb4e1dca713016c518e31` を使用して、アドレス `0x000000006551c19487814612e58FE06813775758` に展開する必要があります。

レジストリは、次のトランザクションを使用して任意の EVM 互換チェーンにデプロイできます。

```json
{
        "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
        "value": "0x0",
        "data": "0x0000000000000000000000000000000000000000fd8eb4e1dca713016c518e31608060405234801561001057600080fd5b5061023b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063246a00211461003b5780638a54c52f1461006a575b600080fd5b61004e6100493660046101b7565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b61004e6100783660046101b7565b6100e1565b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b60015284601552605560002060601b60601c60005260206000f35b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b600152846015526055600020803b61018b578560b760556000f580610157576320188a596000526004601cfd5b80606c52508284887f79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf887226060606ca46020606cf35b8060601b60601c60005260206000f35b80356001600160a01b03811681146101b257600080fd5b919050565b600080600080600060a086880312156101cf57600080fd5b6101d88661019b565b945060208601359350604086013592506101f46060870161019b565b94979396509194608001359291505056fea2646970667358221220ea2fe53af507453c64dd7c1db05549fa47a298dfb825d6d11e1689856135f16764736f6c63430008110033",
}
```

レジストリは、各トークン バインド アカウントを、バイトコードに不変の定数データが追加された [ERC-1167](https://eips.ethereum.org/EIPS/eip-1167) 最小プロキシとして展開しなければなりません (MUST)。

各トークン バインド アカウントのデプロイされたバイトコードは、次の構造でなければなりません。

```text
ERC-1167 Header               (10 bytes)
<implementation (address)>    (20 bytes)
ERC-1167 Footer               (15 bytes)
<salt (bytes32)>              (32 bytes)
<chainId (uint256)>           (32 bytes)
<tokenContract (address)>     (32 bytes)
<tokenId (uint256)>           (32 bytes)
```

たとえば、実装アドレス `0xbebebebebebebebebebebebebebebebebebe`、ソルト `0`、チェーン ID `1`、トークン コントラクト `0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf`、トークン ID `123` のトークン バインド アカウントには、次のデプロイされたバイトコードがあります。

```text
363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf000000000000000000000000000000000000000000000000000000000000007b
```

各トークン バインド アカウント プロキシは、`IERC6551Account` インターフェイスを実装するコントラクトに実行を委任しなければなりません (MUST)。

レジストリは、各アカウント アドレスが決定的になるように、`create2` オペコードを使用してすべてのトークン バインド アカウントをデプロイしなければなりません (MUST)。
各トークン バインドされたアカウント アドレスは、その実装アドレス、トークン コントラクト アドレス、トークン ID、チェーン ID、およびソルトの一意の組み合わせから導出されるものとします (SHALL)。

レジストリは次のインターフェイスを実装する必要があります (MUST)。

```javascript
interface IERC6551Registry {
    /**
     * @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
     */
    event ERC6551AccountCreated(
        address account,
        address indexed implementation,
        bytes32 salt,
        uint256 chainId,
        address indexed tokenContract,
        uint256 indexed tokenId
    );

    /**
     * @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
     */
    error AccountCreationFailed();

    /**
     * @dev Creates a token bound account for a non-fungible token.
     *
     * If account has already been created, returns the account address without calling create2.
     *
     * Emits ERC6551AccountCreated event.
     *
     * @return account The address of the token bound account
     */
    function createAccount(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external returns (address account);

    /**
     * @dev Returns the computed token bound account address for a non-fungible token.
     *
     * @return account The address of the token bound account
     */
    function account(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external view returns (address account);
}
```

## アカウントインターフェース（Account Interface）

すべてのトークン バインドされたアカウントは、シングルトン レジストリを介して作成される必要があります (SHOULD)。

すべてのトークン バインド アカウント実装は、[ERC-165](https://eips.ethereum.org/EIPS/eip-165) インターフェイス検出を実装しなければなりません (MUST)。

すべてのトークン バインド アカウント実装は、[ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) 署名検証を実装しなければなりません (MUST)。

すべてのトークン バインド アカウント実装は、次のインターフェイスを実装する必要があります (MUST)。

```javascript
/// @dev the ERC-165 identifier for this interface is `0x6faff5f1`
interface IERC6551Account {
    /**
     * @dev Allows the account to receive Ether.
     *
     * Accounts MUST implement a `receive` function.
     *
     * Accounts MAY perform arbitrary logic to restrict conditions
     * under which Ether can be received.
     */
    receive() external payable;

    /**
     * @dev Returns the identifier of the non-fungible token which owns the account.
     *
     * The return value of this function MUST be constant - it MUST NOT change over time.
     *
     * @return chainId       The chain ID of the chain the token exists on
     * @return tokenContract The contract address of the token
     * @return tokenId       The ID of the token
     */
    function token()
        external
        view
        returns (uint256 chainId, address tokenContract, uint256 tokenId);

    /**
     * @dev Returns a value that SHOULD be modified each time the account changes state.
     *
     * @return The current account state
     */
    function state() external view returns (uint256);

    /**
     * @dev Returns a magic value indicating whether a given signer is authorized to act on behalf
     * of the account.
     *
     * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid.
     *
     * By default, the holder of the non-fungible token the account is bound to MUST be considered
     * a valid signer.
     *
     * Accounts MAY implement additional authorization logic which invalidates the holder as a
     * signer or grants signing permissions to other non-holder accounts.
     *
     * @param  signer     The address to check signing authorization for
     * @param  context    Additional data used to determine whether the signer is valid
     * @return magicValue Magic value indicating whether the signer is valid
     */
    function isValidSigner(address signer, bytes calldata context)
        external
        view
        returns (bytes4 magicValue);
}

```

## 実行インターフェース（Execution Interface）

すべてのトークン バインド アカウントは、有効な署名者がアカウントに代わって任意の操作を実行できるようにする実行インターフェイスを実装しなければなりません (MUST)。
実行インターフェイスのサポートは、ERC-165 インターフェイス検出を使用してアカウントによって通知されなければなりません。

トークン バインドされたアカウントは、次の実行インターフェイスをサポートしてもよい (MAY)。

```javascript
/// @dev the ERC-165 identifier for this interface is `0x51945447`
interface IERC6551Executable {
    /**
     * @dev Executes a low-level operation if the caller is a valid signer on the account.
     *
     * Reverts and bubbles up error if operation fails.
     *
     * Accounts implementing this interface MUST accept the following operation parameter values:
     * - 0 = CALL
     * - 1 = DELEGATECALL
     * - 2 = CREATE
     * - 3 = CREATE2
     *
     * Accounts implementing this interface MAY support additional operations or restrict a signer's
     * ability to execute certain operations.
     *
     * @param to        The target address of the operation
     * @param value     The Ether value to be sent to the target
     * @param data      The encoded operation calldata
     * @param operation A value indicating the type of operation to perform
     * @return The result of the operation
     */
    function execute(address to, uint256 value, bytes calldata data, uint8 operation)
        external
        payable
        returns (bytes memory);
}
```


## 理論的根拠（Rationale）

### シングルトンレジストリ（Singleton Registry）

この提案では、既知のアドレスにある任意のチェーンに許可なくデプロイできる単一の正規レジストリを指定します。
複数のレジストリ コントラクトによって実装できる共通のインターフェイスを意図的に指定していません。
このアプローチにより、いくつかの重要な特性が有効になります。

#### 虚偽のアカウント（Counterfactual Accounts）

すべてのトークン バインド アカウントは create2 オペコードを使用して作成され、アカウントが作成前の事実に反する状態で存在できるようになります。
これにより、トークン バインドされたアカウントは、コントラクトの作成前にアセットを受け取ることができます。
シングルトン アカウント レジストリにより、すべてのトークン バインドされたアカウント アドレスに共通のアドレス指定スキームが使用されることを保証されます。

#### トラストレスな導入（Trustless Deployments）

単一の所有者なしレジストリにより、トークン バインドされたアカウントに対して唯一信頼できるコントラクトの実装であることが保証されます。
これにより、トークンの所有者は、信頼できる実装を使用して、反事実のアカウント内に保存されているすべての資産にアクセスできることが保証されます。

正規レジストリがない場合、一部のトークン バインド アカウントは、所有レジストリまたはアップグレード可能なレジストリを使用して展開される可能性があります。
これにより、虚偽のアカウントに保存されている資産を失われる可能性があり、この提案をサポートするアプリケーションを考慮する必要があるセキュリティ モデルの範囲が増加します。

#### クロスチェーン互換性（Cross-chain Compatibility）

既知のアドレスを持つシングルトン レジストリにより、各トークン バインド アカウントが複数のチェーン上に存在できます。
`createAccount` へのパラメーターとして `chainId` を含めることにより、トークン バインドされたアカウントのコントラクトを、サポートされているチェーン上の同じアドレスへデプロイできるようになります。
したがって、アカウント実装は、あるチェーン上の NFT が別のチェーン上のトークン バインドされたアカウントを制御できる、クロスチェーン アカウントの実行をサポートできます。

#### 単一のエントリポイント（Single Entry Point）

アカウント アドレスと `AccountCreated` イベントをクエリするための単一のエントリ ポイントにより、この提案をサポートするアプリケーションでトークン バインドされたアカウントのインデックスを作成する複雑なタスクが簡素化されます。

#### 実装の多様性（Implementation Diversity）

シングルトン レジストリを使用すると、さまざまなアカウント実装で共通のアドレス指定スキームを共有できます。
これにより、開発者は、クライアント アプリケーションで簡単にサポートできる方法で、アカウント固有の機能 (委任など) と代替アカウント モデル (一時的なアカウントなど) の両方を実装する大きな自由が得られます。

### レジストリとファクトリー（Registry vs Factory）

「ファクトリー」の代わりに「レジストリ」という用語が選択されたのは、契約の正規の性質を強調し、アカウントの作成 (アカウントごとに 1 回だけ発生) よりもアカウント アドレスのクエリ (定期的に発生) の行為を強調するためです。

### 変数実行インターフェイス（Variable Execution Interface）

この提案では、ERC-165 インターフェイス検出を通じて少なくとも 1 つの実行インターフェイスのサポートを通知する限り、アカウントが互換性を保つために特定の実行インターフェイスを実装する必要はありません。
アカウント開発者が独自の実行インターフェイスを選択できるようにすることで、この提案はさまざまな既存の実行インターフェイスをサポートし、将来標準化される可能性のあるインターフェイスとの上位互換性を維持できます。

### アカウントの曖昧さ（Account Ambiguity）

上記で提案されている仕様では、NFT が複数のトークン バインド アカウントを持つことができます。
この提案の開発中に、単一のトークン バインド アカウントを各 NFT に割り当て、各トークン バインド アカウントのアドレスを明確な識別子にする代替アーキテクチャが検討されました。

ただし、これらの代替案にはいくつかのトレードオフがあります。

まず、スマート コントラクトのパーミッションレスな性質により、NFT ごとに 1 つのトークン バインド アカウントの制限を強制することは不可能です。
NFT ごとに複数のトークン バインド アカウントを利用したい場合は、追加のレジストリ コントラクトを展開することで利用できます。

第二に、各 NFT を単一のトークン バインド アカウントに制限するには、静的で信頼できるアカウントの実装をこの提案に含める必要があります。
この実装では、トークン バインドされたアカウントの機能に特定の制約が必然的に課されます。
この提案によって可能になる未踏のユースケースの数と、多様なアカウント実装が代替不可能なトークンエコシステムにもたらす可能性のある利点を考慮すると、この提案で正規かつ制約された実装を定義するのは時期尚早であるというのが著者の意見です。

最後に、この提案は、NFT にオンチェーンのエージェントとして機能する能力を付与することを目指しています。
現在の実務では、オンチェーン エージェントは複数のアカウントを使用することがよくあります。
一般的な例は、日常使用に「ホット」アカウントを使用し、貴重品の保管に「コールド」アカウントを使用する個人です。
オンチェーンエージェントが一般的に複数のアカウントを使用する場合、NFT が同じ機能を継承する必要があるのは当然です。

### プロキシの実装（Proxy Implementation）

ERC-1167 最小限のプロキシは既存のインフラストラクチャで十分にサポートされており、一般的なスマート コントラクト パターンです。
この提案では、ソルト、チェーン ID、トークン コントラクト アドレス、およびトークン ID をコントラクト バイトコードに追加される ABI エンコード定数データとして保存するカスタム ERC-1167 プロキシ実装を使用して、各トークン バインド アカウントをデプロイします。
これにより、トークン バインドされたアカウント実装は、データを一定に保ちながら、このデータを簡単にクエリできるようになります。
このアプローチは、既存のインフラストラクチャとの互換性を最大限に高めると同時に、カスタム トークン バインド アカウント実装を作成する際スマート コントラクト開発者に完全な柔軟性を与えるために採用されました。

### チェーン識別子（Chain Identifier）

この提案では、チェーン ID を使用して、コントラクト アドレスとトークン ID とともに各 NFT を識別します。
トークン識別子は、単一のイーサリアム チェーン上ではグローバルに一意ですが、複数のイーサリアム チェーン全体では一意ではない場合があります。

## 下位互換性（Backwards Compatibility）

この提案は、既存の代替不可能なトークン契約との最大限の下位互換性を目指しています。
したがって、ERC-721 標準を拡張するものではありません。

さらに、この提案では、アカウント作成前にレジストリが ERC-721 との互換性について ERC-165 インターフェイス チェックを実行する必要はありません。
これにより、ERC-721 標準以前の代替不可能なトークン コントラクト (CryptoKitties など)、または ERC-721 インターフェイスのサブセット (ENS NameWrapper namesなど) のみを実装したトークン コントラクトとの互換性が最大化されます。
また、この提案で説明されているシステムを半代替可能トークンまたは代替可能トークンで使用することもできますが、これらの使用例は提案の範囲外です。

スマート コントラクトの作成者は、アカウント実装で ERC-721 のインターフェイス検出を強制することをオプションで選択できます。

## リファレンス実装（Reference Implementation）
### アカウントの実装例（Example Account Implementation）

```javascript
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

interface IERC6551Account {
    receive() external payable;

    function token()
        external
        view
        returns (uint256 chainId, address tokenContract, uint256 tokenId);

    function state() external view returns (uint256);

    function isValidSigner(address signer, bytes calldata context)
        external
        view
        returns (bytes4 magicValue);
}

interface IERC6551Executable {
    function execute(address to, uint256 value, bytes calldata data, uint8 operation)
        external
        payable
        returns (bytes memory);
}

contract ERC6551Account is IERC165, IERC1271, IERC6551Account, IERC6551Executable {
    uint256 public state;

    receive() external payable {}

    function execute(address to, uint256 value, bytes calldata data, uint8 operation)
        external
        payable
        virtual
        returns (bytes memory result)
    {
        require(_isValidSigner(msg.sender), "Invalid signer");
        require(operation == 0, "Only call operations are supported");

        ++state;

        bool success;
        (success, result) = to.call{value: value}(data);

        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }

    function isValidSigner(address signer, bytes calldata) external view virtual returns (bytes4) {
        if (_isValidSigner(signer)) {
            return IERC6551Account.isValidSigner.selector;
        }

        return bytes4(0);
    }

    function isValidSignature(bytes32 hash, bytes memory signature)
        external
        view
        virtual
        returns (bytes4 magicValue)
    {
        bool isValid = SignatureChecker.isValidSignatureNow(owner(), hash, signature);

        if (isValid) {
            return IERC1271.isValidSignature.selector;
        }

        return bytes4(0);
    }

    function supportsInterface(bytes4 interfaceId) external pure virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId
            || interfaceId == type(IERC6551Account).interfaceId
            || interfaceId == type(IERC6551Executable).interfaceId;
    }

    function token() public view virtual returns (uint256, address, uint256) {
        bytes memory footer = new bytes(0x60);

        assembly {
            extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
        }

        return abi.decode(footer, (uint256, address, uint256));
    }

    function owner() public view virtual returns (address) {
        (uint256 chainId, address tokenContract, uint256 tokenId) = token();
        if (chainId != block.chainid) return address(0);

        return IERC721(tokenContract).ownerOf(tokenId);
    }

    function _isValidSigner(address signer) internal view virtual returns (bool) {
        return signer == owner();
    }
}
```

### レジストリの実装（Registry Implementation）

```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IERC6551Registry {
    /**
     * @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
     */
    event ERC6551AccountCreated(
        address account,
        address indexed implementation,
        bytes32 salt,
        uint256 chainId,
        address indexed tokenContract,
        uint256 indexed tokenId
    );

    /**
     * @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
     */
    error AccountCreationFailed();

    /**
     * @dev Creates a token bound account for a non-fungible token.
     *
     * If account has already been created, returns the account address without calling create2.
     *
     * Emits ERC6551AccountCreated event.
     *
     * @return account The address of the token bound account
     */
    function createAccount(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external returns (address account);

    /**
     * @dev Returns the computed token bound account address for a non-fungible token.
     *
     * @return account The address of the token bound account
     */
    function account(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external view returns (address account);
}

contract ERC6551Registry is IERC6551Registry {
    function createAccount(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external returns (address) {
        assembly {
            // Memory Layout:
            // ----
            // 0x00   0xff                           (1 byte)
            // 0x01   registry (address)             (20 bytes)
            // 0x15   salt (bytes32)                 (32 bytes)
            // 0x35   Bytecode Hash (bytes32)        (32 bytes)
            // ----
            // 0x55   ERC-1167 Constructor + Header  (20 bytes)
            // 0x69   implementation (address)       (20 bytes)
            // 0x5D   ERC-1167 Footer                (15 bytes)
            // 0x8C   salt (uint256)                 (32 bytes)
            // 0xAC   chainId (uint256)              (32 bytes)
            // 0xCC   tokenContract (address)        (32 bytes)
            // 0xEC   tokenId (uint256)              (32 bytes)

            // Silence unused variable warnings
            pop(chainId)

            // Copy bytecode + constant data to memory
            calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
            mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
            mstore(0x5d, implementation) // implementation
            mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header

            // Copy create2 computation data to memory
            mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
            mstore(0x15, salt) // salt
            mstore(0x01, shl(96, address())) // registry address
            mstore8(0x00, 0xff) // 0xFF

            // Compute account address
            let computed := keccak256(0x00, 0x55)

            // If the account has not yet been deployed
            if iszero(extcodesize(computed)) {
                // Deploy account contract
                let deployed := create2(0, 0x55, 0xb7, salt)

                // Revert if the deployment fails
                if iszero(deployed) {
                    mstore(0x00, 0x20188a59) // `AccountCreationFailed()`
                    revert(0x1c, 0x04)
                }

                // Store account address in memory before salt and chainId
                mstore(0x6c, deployed)

                // Emit the ERC6551AccountCreated event
                log4(
                    0x6c,
                    0x60,
                    // `ERC6551AccountCreated(address,address,bytes32,uint256,address,uint256)`
                    0x79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf88722,
                    implementation,
                    tokenContract,
                    tokenId
                )

                // Return the account address
                return(0x6c, 0x20)
            }

            // Otherwise, return the computed account address
            mstore(0x00, shr(96, shl(96, computed)))
            return(0x00, 0x20)
        }
    }

    function account(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external view returns (address) {
        assembly {
            // Silence unused variable warnings
            pop(chainId)
            pop(tokenContract)
            pop(tokenId)

            // Copy bytecode + constant data to memory
            calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
            mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
            mstore(0x5d, implementation) // implementation
            mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header

            // Copy create2 computation data to memory
            mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
            mstore(0x15, salt) // salt
            mstore(0x01, shl(96, address())) // registry address
            mstore8(0x00, 0xff) // 0xFF

            // Store computed account address in memory
            mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55))))

            // Return computed account address
            return(0x00, 0x20)
        }
    }
}
```

## セキュリティに関する考慮事項（Security Considerations）
### 不正防止（Fraud Prevention）

トークンバインドされたアカウントのトラストレス販売を可能にするため、分散型マーケットプレイスは悪意のあるアカウント所有者による不正行為に対する保護措置を実装する必要があります。

次の潜在的な詐欺について考えてみましょう。

- アリスは ERC-721 トークン X を所有しており、トークン バインド アカウント Y を所有しています
- アリスはアカウント Y に 10ETH を入金します
- ボブは、アカウント Y に保管されている 10ETH をトークンと一緒に受け取ることを前提として、分散型市場経由でトークン X を 11ETH で購入することを提案します
- アリスはトークンにバインドされたアカウントから 10ETH を引き出し、すぐにボブのオファーを受け入れます
- ボブはトークン X を受け取りますが、アカウント Y は空です

悪意のあるアカウント所有者による詐欺行為を軽減するために、分散型マーケットプレイスは、この種の詐欺に対する保護をマーケットプレイス レベルで実装する必要があります (SHOULD)。
この EIP を実装するコントラクトは、不正行為に対する特定の保護を実装してもよい(MAY)。

考慮すべきいくつかの緩和戦略を次に示します。

- 現在のトークン バインドされたアカウントの状態をマーケットプレイスの注文に添付します<br>注文後にアカウントの状態が変化した場合、オファーは無効であるとみなしてください<br>この機能はマーケットプレイス レベルでサポートされる必要があります
- 注文が履行されたときトークンバインドされたアカウントに残ることが期待される資産コミットメントのリストをマーケットプレイスの注文に添付します<br>注文後コミットされた資産のいずれかがアカウントから削除された場合、オファーは無効であるとみなしてください<br>これは市場でも実装する必要があります
- 注文の署名を検証する前に上記のロジックを実行する外部スマート コントラクトを介して注文を分散型市場に送信します<br>これにより、マーケットプレイスのサポートなしで安全な転送を実装できるようになります
- トークン バインドされたアカウント実装にロック メカニズムを実装し、悪意のある所有者がロック中にアカウントから資産を抽出できないようにします

詐欺の防止はこの提案の範囲外です。

## 所有権サイクル（Ownership Cycles）

所有権サイクルが作成されると、トークン バインドされたアカウントに保持されているすべての資産にアクセスできなくなる可能性があります。
最も単純な例は、ERC-721 トークンが独自のトークン バインドされたアカウントに転送される場合です。
これが発生した場合、トークン バインド アカウントは ERC-721 トークンを転送するトランザクションを実行できないため、ERC-721 トークンとトークン バインド アカウントに保存されているすべての資産の両方に対し永久にアクセスできなくなります。

所有権サイクルは、n>0 のトークン バインド アカウントのグラフに導入できます。
深さが 1 を超えるサイクルのオンチェーン防止は、必要な無限の検索スペースを考慮すると強制するのが難しいため、この提案の範囲外です。
この提案の採用を希望するアプリケーション クライアントとアカウント実装には、所有権サイクルの可能性を制限する措置を実装することが推奨されます。

## 著作権（Copyright）

著作権および関連する権利は [CC0](https://eips.ethereum.org/LICENSE) を通じて放棄されます。

## 引用（Citation）

この文書を次のように引用してください。

Jayden Windle (@jaydenwindle), Benny Giang <bg@futureprimitive.xyz>, Steve Jang, Druzy Downs (@druzydowns), Raymond Huynh (@huynhr), Alanah Lam <alanah@futureprimitive.xyz>, Wilkins Chung (@wwhchung) <wilkins@manifold.xyz>, Paul Sullivan (@sullivph) <paul.sullivan@manifold.xyz>, Auryn Macmillan (@auryn-macmillan), Jan-Felix Schwarz (@jfschwarz), Anton Bukov (@k06a), Mikhail Melnik (@ZumZoom), Josh Weintraub (@jhweintraub) <josh@revest.finance>, Rob Montgomery (@RobAnon) <rob@revest.finance>, vectorized (@vectorized), "ERC-6551: Non-fungible Token Bound Accounts [DRAFT]," Ethereum Improvement Proposals, no. 6551, February 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6551.

# まとめ（Conclusion）

内容はなんとなく理解できました。
つぎは、実際に動かしてみようと思います。