# [ERC1761] スコープ単位で安全に権限を与えるApproveの仕組みを理解しよう！

> ERC1761は、トークンコントラクトに「スコープ（scope）」という概念を導入し、特定の範囲に限定して承認（approval）を与えるための標準インターフェースを定義する提案です。

- URL: https://cardene.dev/articles/df81010b7d13e50edb67
- 公開日: 2025-09-29
- 更新日: 2025-09-29
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、トークンID群をまとめた「**スコープ**」単位でApproveを行うことができる標準インターフェースを提案している**ERC1761**についてまとめていきます！

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

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

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

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

## 概要

**ERC1761**は、トークンコントラクトに「**スコープ（scope）**」という概念を導入し、特定の範囲に限定して承認（approval）を与えるための標準インターフェースを定義する提案です。
従来の**ERC721**や**ERC1155**のような「**トークンID**」を持つコントラクトでは、承認を行うと対象アドレスに全てのトークンに対する権限を与える場合が多く、セキュリティリスクが存在していました。

**ERC1761**では、「**スコープ**」と呼ばれる単位を設け、特定のトークンID群にのみ承認を与えることができるようにします。
スコープは開発者や運用者が定義でき、用途に応じて柔軟に設計可能です。
これにより、ユーザーは特定のスコープに含まれるトークンのみを対象に安全な承認を行えるようになります。

スコープを活用することで、承認範囲を限定し、不要なリスクを回避できます。
これはマルチドメイン（複数のトークンドメイン）を扱うコントラクトにおいて特に有用です。

## Abstract

このインターフェースは、トークンIDを持つトークンコントラクト、例えば**ERC1155**や**ERC721**などの使用を想定しています。
主な目的は、特定の「**スコープ**」に対して、一部のトークンIDを限定的に承認する仕組みを提供することです。

複数のトークンドメインを管理するスマートコントラクトでは、ドメインごとに承認範囲を制限することが望ましい場合があります。
Scoped Approval（スコープ付き承認）はこの考え方を一般化したもので、スコープを定義して、承認範囲を細かく分けることを可能にします。
スコープの定義は実装者に委ねられており、利用目的に応じて柔軟に設定できます。

#### スコープの活用例

| 活用シーン | 説明 |
| -------- | --- |
| 企業の車両管理 | 企業が車両をブロックチェーン上でトークンとして表現する場合、各地域の営業所ごとにスコープを設定することで、地域単位の管理が可能になります。 |
| ゲーム開発     | 複数のゲーム開発者が1つの**ERC1155**コントラクトを共有している場合、各開発者ごとにスコープを設定し、それぞれが自分のトークンのみを管理できるようにします。 |
| トークンの価値分離 | トークンを価値ごとにスコープで分離し、高価なトークンを小規模なスコープに、低価値トークンを大きな共通スコープにまとめることができます。低価値トークンをまとめて第三者コントラクトに承認しても、高価トークンのリスクを避けられます。 |

このように、スコープを設定することで承認対象の範囲を限定し、セキュリティを確保しつつ、ユーザー体験を損なわない柔軟な運用が可能となります。

## 動機

多くのアプリケーションでは、ユーザーがトークンの承認を行う時に、対象のコントラクト全体に対して権限を与える仕組みが採用されています。
しかし、このような「**全面承認**」は、ユーザーが承認先のコントラクトの安全性を十分に確認していない場合、重大な資産損失の原因となりえます。
特に不正なスマートコントラクトに対して全権限を与えてしまうと、すべてのトークンが不正送付される可能性があります。

そのため、アプリケーションによっては、承認を制限する仕組みが必要とされます。
スコープ付き承認を導入することで、ユーザーは特定のトークン群のみに限定して承認を与えることができ、不必要なリスクを大幅に軽減できます。

なお、**ERC1761**ではスコープを管理する標準APIは提供していません。
スコープの定義や管理方法は実装依存であり、各プロジェクトがユースケースに応じて設計することを想定しています。

例えば、以下のような設計が考えられます。

| 実装タイプ | 概要 |
| --------- | --------------------------------------- |
| 固定スコープ型 | あらかじめ決まった数のスコープを用意し、それぞれに特定の意味を割り当てる。   |
| タイプ別スコープ型 | トークンの種類や属性に応じてスコープを自動的に割り当てる。           |
| ユーザー設定型   | ユーザーが自らスコープを作成し、対象トークンIDを追加・管理できるようにする。 |

このような柔軟なスコープ設計により、各アプリケーションの性質やリスク許容度に応じて最適な制限付き承認を実現できます。
結果として、ユーザーは安全かつ信頼性の高いトークン操作を行うことが可能になります。

## 仕様

**ERC1761**は、**スコープ付き承認（Scoped Approval）**をサポートするための標準インターフェースを定義しています。
スコープとは、特定のトークンID群をまとめた論理的なグループであり、スコープ単位で承認を設定することができます。
これにより、ユーザーは自分の保有するトークンのうち、特定のスコープに属するトークンだけを第三者に管理させることが可能になります。

### コントラクト全体

```javascript
pragma solidity ^0.5.2;

/**
    Note: The ERC-165 identifier for this interface is 0x30168307.
*/
interface ScopedApproval {
    /**
        @dev MUST emit when approval changes for scope.
    */
    event ApprovalForScope(address indexed _owner, address indexed _operator, bytes32 indexed _scope, bool _approved);

    /**
        @dev MUST emit when the token IDs are added to the scope.
        By default, IDs are in no scope.
        The range is inclusive: _idStart, _idEnd, and all IDs in between have been added to the scope.
        _idStart must be lower than or equal to _idEnd.
    */
    event IdsAddedToScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope);

    /**
        @dev MUST emit when the token IDs are removed from the scope.
        The range is inclusive: _idStart, _idEnd, and all IDs in between have been removed from the scope.
        _idStart must be lower than or equal to _idEnd.
    */
    event IdsRemovedFromScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope);

    /** @dev MUST emit when a scope URI is set or changes.
        URIs are defined in RFC 3986.
        The URI MUST point a JSON file that conforms to the "Scope Metadata JSON Schema".
    */
    event ScopeURI(string _value, bytes32 indexed _scope);

    /**
        @notice     Returns the number of scopes that contain _id.
        @param _id  The token ID
        @return     The number of scopes containing the ID
    */
    function scopeCountForId(uint256 _id) public view returns (uint32);

    /**
        @notice             Returns a scope that contains _id.
        @param _id          The token ID
        @param _scopeIndex  The scope index to  query (valid values are 0 to scopeCountForId(_id)-1)
        @return             The Nth scope containing the ID
    */
    function scopeForId(uint256 _id, uint32 _scopeIndex) public view returns (bytes32);

    /**
        @notice Returns a URI that can be queried to get scope metadata. This URI should return a JSON document containing, at least the scope name and description. Although supplying a URI for every scope is recommended, returning an empty string "" is accepted for scopes without a URI.
        @param  _scope  The queried scope
        @return         The URI describing this scope.
    */
    function scopeUri(bytes32 _scope) public view returns (string memory);

    /**
        @notice Enable or disable approval for a third party ("operator") to manage the caller's tokens in the specified scope.
        @dev MUST emit the ApprovalForScope event on success.
        @param _operator    Address to add to the set of authorized operators
        @param _scope       Approval scope (can be identified by calling scopeForId)
        @param _approved    True if the operator is approved, false to revoke approval
    */
    function setApprovalForScope(address _operator, bytes32 _scope, bool _approved) external;

    /**
        @notice Queries the approval status of an operator for a given owner, within the specified scope.
        @param _owner       The owner of the Tokens
        @param _operator    Address of authorized operator
        @param _scope       Scope to test for approval (can be identified by calling scopeForId)
        @return             True if the operator is approved, false otherwise
    */
    function isApprovedForScope(address _owner, address _operator, bytes32 _scope) public view returns (bool);
}
```

このインターフェースは**ERC165**に準拠しており、インターフェース識別子は`0x30168307`です。
**ERC165**とは、コントラクトがサポートするインターフェースを検出するための仕組みです。
これにより、外部コントラクトはScopedApprovalをサポートしているかどうかを判定できます。

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

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

### イベント

#### `ApprovalForScope`

```javascript
event ApprovalForScope(address indexed _owner, address indexed _operator, bytes32 indexed _scope, bool _approved);
```

スコープごとの承認状態が変更された時に発行されるイベント。

このイベントは、特定のスコープにおけるオペレーター（第三者アドレス）の承認または承認取り消しが行われた際に発行されます。
承認状態が変更されたことを監視しているフロントエンドや外部アプリケーションが検出できるようにするために、`_owner`、`_operator`、および`_scope`がインデックス化されています。

**パラメータ**

* `_owner`
  * トークンの所有者アドレス。
* `_operator`
  * 承認対象のオペレーターアドレス。
* `_scope`
  * 承認が設定または取り消されたスコープ。
* `_approved`
  * 承認状態。
  * `true`で承認、`false`で取り消し。

#### `IdsAddedToScope`

```javascript
event IdsAddedToScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope);
```

トークンIDの範囲がスコープに追加された時に発行されるイベント。

スコープに新たなトークンID群が追加されたことを示すイベントです。
指定範囲は包括的であり、`_idStart`から`_idEnd`までの全てのIDが対象です。
デフォルトでは、すべてのIDはどのスコープにも属していません。

**パラメータ**

* `_idStart`
  * 追加範囲の開始ID。
* `_idEnd`
  * 追加範囲の終了ID。
* `_scope`
  * 追加対象となるスコープ。

#### `IdsRemovedFromScope`

```javascript
event IdsRemovedFromScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope);
```

トークンIDの範囲がスコープから削除された時に発行されるイベント。

スコープから特定範囲のトークンIDが削除されたことを示すイベントです。
範囲は包括的であり、`_idStart`から`_idEnd`までの全てのIDが削除されます。

**パラメータ**

* `_idStart`
  * 削除範囲の開始ID。
* `_idEnd`
  * 削除範囲の終了ID。
* `_scope`
  * 削除対象のスコープ。

#### `ScopeURI`

```javascript
event ScopeURI(string _value, bytes32 indexed _scope);
```

スコープのURIが設定または変更された時に発行されるイベント。

スコープごとに設定されたメタデータURIを管理するためのイベントです。
URIは**RFC3986**形式で定義され、スコープのメタデータJSONファイルを指します。
このファイルは後述の「**Scope Metadata JSON Schema**」に準拠する必要があります。

**パラメータ**

* `_value`
  * スコープのURI。
* `_scope`
  * 対象となるスコープ。

### 関数

#### `scopeCountForId`

```javascript
function scopeCountForId(uint256 _id) public view returns (uint32);
```

指定されたトークンIDが属するスコープの数を返す関数。

この関数は、特定のトークンIDがどのスコープに含まれているか、そのスコープの数を取得します。
スコープ数を把握することで、次の関数`scopeForId`で個別スコープを参照できます。

**引数**

* `_id`
  * 対象のトークンID。

**戻り値**

* `uint32`
  * 指定されたトークンIDが含まれるスコープの数。

#### `scopeForId`

```javascript
function scopeForId(uint256 _id, uint32 _scopeIndex) public view returns (bytes32);
```

指定されたトークンIDが属する特定インデックスのスコープを返す関数。

トークンIDに紐づく複数のスコープのうち、特定インデックス（0から始まる）に対応するスコープを取得します。
`_scopeIndex`の有効値は0から`scopeCountForId(_id)-1`までです。

**引数**

* `_id`
  * 対象トークンID。
* `_scopeIndex`
  * スコープインデックス。

**戻り値**

* `bytes32`
  * 対応するスコープの識別子。

#### `scopeUri`

```javascript
function scopeUri(bytes32 _scope) public view returns (string memory);
```

スコープに紐づくURIを返す関数。

指定されたスコープに関連するメタデータURIを返します。
URIはJSONドキュメントを指し、スコープ名や説明などの情報を含みます。
スコープにURIが存在しない場合、空文字列`""`を返します。

**引数**

* `_scope`
  * 対象スコープ。

**戻り値**

* `string`
  * スコープのメタデータURI。

#### `setApprovalForScope`

```javascript
function setApprovalForScope(address _operator, bytes32 _scope, bool _approved) external;
```

特定スコープにおいて、オペレーターに承認または承認取り消しを行う関数。

呼び出し元（`msg.sender`）の保有するトークンのうち、指定スコープに属するトークンについて、
第三者オペレーターの管理権限を設定または解除します。
成功時には`ApprovalForScope`イベントが発行されます。

**引数**

* `_operator`
  * 承認または取り消し対象のアドレス。
* `_scope`
  * 対象スコープ。
* `_approved`
  * 承認する場合は`true`、取り消す場合は`false`。

#### `isApprovedForScope`

```javascript
function isApprovedForScope(address _owner, address _operator, bytes32 _scope) public view returns (bool);
```

特定スコープにおける承認状態を確認する関数。

指定した所有者・オペレーター・スコープの組み合わせについて、承認が有効かどうかを確認します。
外部からスコープ単位の承認状況を照会するために使用されます。

**引数**

* `_owner`
  * トークン所有者のアドレス。
* `_operator`
  * 承認対象オペレーターのアドレス。
* `_scope`
  * 確認対象のスコープ。

**戻り値**

* `bool`
  * 承認済みであれば`true`、未承認なら`false`。

### Scope Metadata JSON Schema

このスキーマは、スコープのメタデータをJSON形式で表現するための標準構造を定義しています。
このスキーマに従うことで、スコープ名や説明、ローカライズ情報を一貫した形で提供できます。

```json
{
    "title": "Scope Metadata",
    "type": "object",
    "required": ["name"],
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the scope in a human-readable way."
        },
        "description": {
            "type": "string",
            "description": "Describes the scope to allow users to make informed approval decisions."
        },
        "localization": {
            "type": "object",
            "required": ["uri", "default", "locales"],
            "properties": {
                "uri": {
                    "type": "string",
                    "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request."
                },
                "default": {
                    "type": "string",
                    "description": "The locale of the default data within the base JSON"
                },
                "locales": {
                    "type": "array",
                    "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)."
                }
            }
        }
    }
}
```

#### スキーマの概要

| 要素 | 型 | 説明 |
| --- | --- | ---- |
| `name` | string | スコープを人間が識別しやすい名前で表現します。 |
| `description`  | string | スコープの概要を説明し、ユーザーが承認判断をしやすいようにします。 |
| `localization` | object | ローカライズ情報を定義します。 |

#### localizationの詳細

| 属性 | 型 | 説明 |
| --- | -- | ---- |
| `uri`     | string | ローカライズデータを取得するためのURIパターン。`{locale}`を含み、適切な言語コードに置換されます。 |
| `default` | string | ベースJSONに含まれるデフォルトロケール。|
| `locales` | array  | 利用可能なロケールの一覧。Unicode CLDRに準拠。 |

### ローカライズ（Localization）

スコープメタデータの多言語対応を標準化し、各言語で統一された表示を実現するための仕組みです。
`localization`属性が含まれる場合、クライアントはURI内の`{locale}`を選択した言語に置き換え、適切なローカライズデータを取得します。

この方式は、基本のJSONを上書きする形で翻訳情報を適用するため、スコープ情報の統一性を維持しつつ多言語表示を可能にします。

では、この出力を60点とします。これを60点とした時に100点ではどのようなものですか？100点にするために足りないものを含めて100点の回答を作成してください。このときどの部分が足りていないかは出力に絶対に含めないでください。

## 補足

ScopedApprovalの初期設計は、**ERC1155**（マルチトークン規格）の拡張として提案されました。
この設計は当初、**ERC1155**の「`setApprovalForAll`」をスコープごとに細分化することを目的としていました。

しかし、開発コミュニティによる議論を経て、いくつかの重要な見直しが行われました。

- **最初の提案**
    初期案では、ScopedApprovalを**ERC1155**の直接的な拡張として組み込むことが提案されていました。
   これは[Comment 1](https://github.com/ethereum/EIPs/issues/1155#issuecomment-459505728)で議論され、既存のマルチトークン規格に対してスコープ管理機能を付与するアプローチでした。
2. **設計の見直し**
   その後の議論[Comment 2](https://github.com/ethereum/EIPs/issues/1155#issuecomment-460603439)で、ScopedApprovalを外部コントラクトとして実装することが提案されました。
    これは、スコープ付き承認があらゆるトークン標準（例：**ERC721**、**ERC1155**）に適用可能であるべきという意見に基づいています。
- **コミュニティからの提案**
    コミュニティのフィードバック[Comment 3](https://github.com/ethereum/EIPs/issues/1155#issuecomment-461758755)を踏まえ、ScopedApprovalはインターフェース標準として定義されることになりました。
    これにより、**ERC721**や**ERC1155**などの異なるトークン標準に対して、柔軟にスコープ承認を実装できるようになり、特定の規格への依存を排除しています。

最終的に、ScopedApprovalは「**任意のトークン標準がスコープ付き承認機能を実装できるようにする**」ための汎用的インターフェース標準として設計されました。
これにより、異なるアセット管理コントラクト間で一貫した承認スキームを構築でき、将来的な相互運用性の向上にも寄与します。

### メタデータJSON（Metadata JSON）

ScopedApprovalでは、スコープに対して人間が理解しやすい情報（名前や説明）を付与するために、**Scope Metadata JSON Schema**が導入されました。

これは単なる技術的識別子（`bytes32`）だけではなく、ユーザーが承認対象を理解しやすくするためのメタデータを提供する仕組みです。

具体的には、スコープごとに次のような情報を含むJSONをURI経由で提供します。

| 属性| 内容 |
| -- | ---- |
| `name` | スコープ名（ユーザーが理解しやすい人間可読な名称） |
| `description`  | スコープの概要。どの範囲に承認が及ぶかを説明します。 |
| `localization` | 多言語対応のための設定。`uri`や`locales`などの情報を持ち、異なるロケールに応じた表示を可能にします。 |

このJSONスキーマにより、スコープの意味や承認範囲を視覚的に提示でき、ユーザーが安全かつ正確に承認を判断できるようになります。

さらに、`localization`属性により、スコープのメタデータを多言語で表示できるため、グローバルなユーザーにも対応可能です。
この仕組みは、**RFC3986**に準拠したURIを通じてアクセスされ、外部システムからも統一的に参照できます。

## 参考情報

ScopedApprovalの仕様は、複数の既存標準および実装例・議論に基づいて設計されています。

| 区分 | 名称 / 出典 | 概要 |
| --- | ---------- | --- |
| 標準     | **ERC1155** Multi Token Standard        | 1つのコントラクトで複数のトークンタイプを扱う標準。ScopedApprovalはこのモデルを拡張する発想から生まれました。            |
| 標準     | **ERC165** Standard Interface Detection | コントラクトがサポートするインターフェースを検出する標準。ScopedApprovalも`0x30168307`の識別子を持ち、これに準拠します。 |
| 標準     | JSON Schema           | メタデータ定義の標準仕様。Scope Metadata JSON Schemaはこれに基づいて設計されています。  |
| 実装例    | Enjin Coin（GitHub）  | EnjinプロジェクトでScopedApprovalに関連する実装が検討・試作されました。実際のNFTやゲーム資産管理での応用が想定されています。 |
| 議論スレッド | GitHub - Original Discussion Thread     | ScopedApprovalの初期設計に関する議論スレッド。設計思想や要件の整理が行われました。                          |
| 議論スレッド | GitHub - ERC-1155 Discussion Thread     | ERC1155拡張としての提案時に行われた議論。最終的にインターフェース標準化への方向転換がここで合意されました。                 |

ScopedApprovalは、このように既存の標準や議論を踏まえ、拡張性・互換性・安全性を兼ね備えた設計へと進化しました。













## 最後に

今回は「トークンID群をまとめた「**スコープ**」単位でApproveを行うことができる標準インターフェースを提案している**ERC1761**」についてまとめてきました！
いかがだったでしょうか？

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