# [ERC7715] ウォレットがDAppに実行権限を委ねる仕組みを理解しよう！

> ERC7715は、DApp（分散型アプリケーション）がウォレットに対して「特定の権限を付与してほしい」と要求するための、新しいJSON-RPCメソッドである wallet_grantPermissions を定義します。

- URL: https://cardene.dev/articles/7b4792f84798c9cee323
- 公開日: 2026-01-07
- 更新日: 2026-01-07
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、ウォレットがDAppに対して実行権限を付与し、ユーザーの代わりにトランザクションを送信できる仕組みを提案している**ERC7715**についてまとめていきます！

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

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

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

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

## 概要

**ERC7715**は、DApp（分散型アプリケーション）がウォレットに対して
「**特定の権限を付与してほしい**」と要求するための、新しいJSON-RPCメソッドである `wallet_grantPermissions` を定義します。

ここでいうJSON-RPCは、DAppとウォレットが通信するための一般的なリモート呼び出し方式です。
Ethereumのウォレット連携では、すでに広く使われています。

**ERC7715**の目的は、「**ユーザー本人が毎回トランザクションを署名しなくても、条件付きでウォレットが処理を許可できる仕組み**」を標準化することです。

これにより、これまで実現が難しかった操作が可能になります。

### 想定されている2つのユースケース

#### ウォレット未接続ユーザーのトランザクション実行

**ERC7715**では、ユーザーが常にウォレットを接続していなくても、事前に許可された範囲内でトランザクションを実行できます。

これは、以下のようなケースを可能にします。

| ユースケース    | 説明                       |
| --------- | ------------------------ |
| サブスクリプション | 定期的な支払いを、ユーザー操作なしで実行できます |
| 積立・自動投資   | あらかじめ決めたルールに従って自動実行できます  |
| 指値注文      | 価格条件を満たしたときに自動で取引できます    |

従来は、ウォレットが接続されていない状態ではトランザクションを送れませんでした。
**ERC7715**は、この制約を根本的に解消します。

#### 権限でスコープされたウォレット接続

**ERC7715**では、「**この操作だけ**」、「**このコントラクトだけ**」、「**この期間だけ**」といった**限定された権限（スコープ）** をウォレットがDAppに付与できます。

その結果、DAppは付与された範囲内でのみトランザクションを実行できます。
これは、フル権限を常に渡す従来方式と比べて、安全性と使いやすさの両方を改善します。

## 動機

![approve-flow.png](/images/qiita-66056cb9-c820-4598-a21f-760cb5405ede.webp)
https://eips.ethereum.org/EIPS/eip-7715#signer--permission-types

#### 現在の一般的なウォレット承認フロー

現在、多くのDAppは以下のような流れで動作しています。

1. ユーザーがDAppで操作を行います。
2. ウォレットが毎回ポップアップします。
3. ユーザーがトランザクション内容を確認し、署名します。
4. トランザクションが送信されます。

この方式はシンプルですが、明確な問題があります。

##### ユーザー体験の問題

インタラクションが多いアプリケーション、特にゲームやソーシャル系DAppでは、
操作のたびにウォレット承認が必要になります。

その結果、以下の問題が発生します。

* 操作が途切れやすくなります
* ユーザーが疲れて離脱しやすくなります

##### ウォレット未接続時に何もできない問題

現状の仕組みでは、ウォレットが接続されていない限りトランザクションは一切送信できません。

そのため、以下のWeb2では当たり前の機能が実現できません。

* 定期実行
* 条件付き実行
* バックグラウンド処理

この制約が、DAppの設計を大きく制限しています。

#### AA（Account Abstraction）における課題

**AA（Account Abstraction）**とは、EOA（秘密鍵1本で操作する従来のアカウント）ではなく、スマートコントラクトによってアカウントの挙動を柔軟に定義する考え方です。

AAの文脈では、セッションキーという仕組みがよく使われます。

##### セッションキーとは何か

セッションキーとは、以下の特徴を持つ鍵です。

* 一時的に有効
* 実行できる操作が限定されている

例えば、以下の使い方がされます。

* ゲーム操作専用
* 特定コントラクト専用
* 有効期限付き

##### ベンダー依存の問題

現状では、セッションキーの実装方法がウォレットやサービスごとに異なります。

そのため、DApp側が統一的に「**権限をください**」と要求できず、ウォレットごとに個別対応が必要になります
これは、エコシステム全体の拡張性を下げる原因になっています。

#### ERC7715が解決しようとしていること

**ERC7715**は、以下を標準化します。

* 権限付与の要求方法
* ウォレットが権限を承認するためのインターフェース

これにより、DAppはウォレットの実装差異を意識せずに権限を要求できます。
また、ウォレットは安全に権限管理を行えず、ユーザー体験が大きく改善されます。

結果として、Web2に近い操作感を持つDAppを、Web3の安全性を保ったまま実現できるようになります。

## 仕様

### `wallet_grantPermissions`

**ERC7715**は、DAppがウォレットに対して「**この範囲でユーザーの代わりに実行してよい**」という権限をリクエストするために、JSON-RPCメソッド `wallet_grantPermissions` を定義します。

ここでいう「**権限**」は、トランザクション送信などの行動を、時間や範囲を区切って許可するものです。

#### 権限スキーマ

##### 型定義

`wallet_grantPermissions` の `params` は、`PermissionRequest` の配列です（複数チェーンや複数権限をまとめて申請できます）。

```ts
type PermissionRequest = {
  chainId: Hex; // hex-encoding of uint256
  address?: Address;
  expiry: number; // unix timestamp
  signer: {
    type: string; // enum defined by ERCs
    data: Record<string, any>;
  };
  permissions: {
    type: string; // enum defined by ERCs
    data: Record<string, any>;
  }[];
}[];
```

用語も含めて、各フィールドを説明します。

##### フィールドの意味

| 項目名 | フィールド     | 必須 | 説明    |
| --------- | ------------- | -: | ------ |
| チェーンID    | `chainId`     | 必須 | **EIP155**の `chainId` を指定します。ここで指定したチェーンに対する権限リクエストになり、以降のアドレスなどもこのチェーン文脈で解釈されます。  |
| 対象アカウント   | `address`     | 任意 | 権限を付与する対象アカウント（ユーザーのアカウント）です。すでにウォレット接続があり、複数アカウントが公開されている場合に「**どのアカウントに対する許可か**」を明確にできます。省略した場合は、ユーザーがウォレット側で選ぶ前提になります。 |
| 有効期限 | `expiry`      | 必須 | UNIX タイムスタンプ（秒）で、セッションが失効する時刻です。**ERC7715**では、このセッションはその時刻までに 必ず失効する必要があります。      |
| 署名者（サイナー） | `signer`      | 必須 | 権限に紐づく「**実行主体**」を表します。キーやアカウントを指定する場合もあれば、ウォレットがセッション管理を引き受ける場合もあります。  |
| 権限一覧      | `permissions` | 必須 | `signer` が対象アカウントの代わりに実行できる「**許可された振る舞い**」を配列で渡します。   |

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

[\[EIP155\] Ethereumネットワークでリプレイ攻撃を防ぐ仕組みを理解しよう！](/articles/2526432899353e4a0225)

「**hex-encoding of uint256**」は「**uint256（0〜2^256-1 の整数）を 16進表現（0x付き）で表す**」という意味です。
仕様上 `chainId: Hex` になっていますが、例では数値 `123` が置かれています。
ここは実装時に「**要求どおりHexで送る**」か「**ウォレットが受け付ける表現に寄せる**」かを揃える必要があります（仕様としてはHexを意図しています）。

#### リクエスト例

**ERC7715**では、`wallet_grantPermissions` の `params` に `PermissionRequest` の配列を渡します。

例は「**特定アカウントに対し、ネイティブトークン送金（ETH のような通貨）の上限付き許可を期限付きで与える**」イメージです。

```js
[
  {
    chainId: 123,
    address: '0x...',
    expiry: 1577840461,
    signer: {
      type: 'account',
      data: {
        address:'0x016562aA41A8697720ce0943F003141f5dEAe006',
      }
    },
    permissions: [
      {
        type: 'native-token-transfer',
        data: {
          allowance: '0x1DCD6500'
        }
      }
    ],
  }
]
```

ここで出てくる `allowance` は一般的には「**上限額**」を意味しますが、**ERC7715**では意味の詳細は “Permissions” セクションに委ねられている前提です。そのためこの記事では「この permission の `data` に含まれる上限らしき値」という説明に留めます。

#### レスポンス仕様

##### 型定義

返り値（`result`）は `PermissionResponse` の配列で、リクエストで送った内容をベースに、追加情報が付いた形になります。

```ts
type PermissionResponse = PermissionRequest & {
  context: Hex;
  accountMeta?: {
    factory: `0x${string}`;
    factoryData: `0x${string}`;
  };
  signerMeta?: {
    // 7679 userOp building
    userOpBuilder?: `0x${string}`;
    // 7710 delegation
    delegationManager?: `0x${string}`;
  };
};
```

##### 「リクエストと同じとは限らない」点が重要

**ERC7715**は「**レスポンスには元のリクエストの全パラメータが含まれる**」としつつ、「**返ってきた値が、要求した値と同じである保証はない**」と明言しています。

つまり、DApp は「**要求した値**」ではなく「**返ってきた値**」を正として扱う必要があります。
例えば、期限が延びたり短くなったり許可上限が調整されたりする可能性があります。

##### `context`

`context` は、付与された権限を識別するための「**まとめID**」のような役割です。
権限の取り消し（後述の `wallet_revokePermissions`）や `userOp` の送信などで参照できるようにするためのフィールドで、識別情報以外のデータが入ることもあります。

また `context` は、**ERC7679**や**ERC7710**で定義されるコンテキストを入れてよい（**MAY**）とされています。

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

[\[ERC7679\] ERC4337のUserOperationを標準化する仕組みを理解しよう！](/articles/4a66dbe19e76ab5b5b7e)

`userOp` は**ERC4337**系で使う**User Operation**のことで、通常のトランザクションとは別フォーマットの実行リクエストです（AA でよく出ます）。

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

https://zenn.dev/heku/books/88e65212339061/viewer/77d826

[\[ERC4337\] プロトコルの変更をせずにAAを実装する仕組みを理解しよう！](/articles/8c97ca7c93c4557ebfc2)

#### `accountMeta`

`accountMeta` は任意ですが、存在する場合は `factory` と `factoryData` が必須で、**ERC4337**に沿った形で返す必要があります。
2つは「**両方ある**」か「**両方ない**」かのどちらかです。

そして重要なのが以下です。

* アカウントがまだデプロイされていない場合、ウォレットは必ず `accountMeta` を返します。
* その場合、DApp は **MUST**（必ず） `factory` コントラクトを `factoryData` を calldata として呼び出して、アカウントをデプロイしなければなりません。

「`factory`」はアカウントを作るコントラクト、「`factoryData`」はその呼び出しに必要なデータ（`calldata`）です。
`calldata` は「**コントラクト呼び出し時に渡す入力データ**」のことです。

#### signerMeta

`signerMeta` はアカウントタイプ次第で必要になる追加情報です。

| `signer.type` の想定  | `signerMeta` の要件  | 何のためか      |
| ---------------- | ----------------------- | ---------- |
| `wallet`         | 不要    | ウォレットがセッション管理を担うので追加メタがいらない、という扱いです。         |
| `key` または `keys` | `userOpBuilder` が必須     | **ERC7679** で定義される `userOp` 構築情報が必要になります。     |
| `account`        | `delegationManager` が必須 | **ERC7710**の委任（delegation）管理に必要な情報が要求されます。 |

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

[\[ERC7679\] ERC4337のUserOperationを標準化する仕組みを理解しよう！](/articles/4a66dbe19e76ab5b5b7e)

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

[\[ERC7710\] スマートコントラクトで権限を安全に委譲する仕組みを理解しよう！](/articles/0dad0829753e578c2086)

「`delegation`」は「**あるアカウントが別の主体に権限を委任して実行させる**」考え方です。

##### エラー（リクエスト不正・拒否）

リクエストが壊れている、またはウォレットが権限付与できない／したくない場合、ウォレットは**ERC1193**で定義されたコードのエラーを返さなければなりません。

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

[\[ERC1193\] Ethereum Provider APIの仕組みを理解しよう！](/articles/9e796da49299f57bd5c7)

**ERC1193**は、Ethereum Provider（ブラウザの `window.ethereum` など）周りの標準的なエラー形式を定義している規格です。

#### `wallet_grantPermissions` のレスポンス例

レスポンスは `PermissionResponse` 配列で、例では以下がポイントです。

* `expiry` がリクエストより後ろ（1577840461 → 1577850000）に変更されています。
* `allowance` が大きい値に変更されています。
* `context` が追加されています。

```js
[
  {
    // original request with modifications
    chainId: 123,
    address: '0x...',
    expiry: 1577850000,
    signer: {
      type: 'account',
      data: {
        address:'0x016562aA41A8697720ce0943F003141f5dEAe006',
      }
    },
    permissions: [
      {
        type: 'native-token-transfer',
        data: {
          allowance: '0x1DCD65000000'
        }
      },
    ],
    // response-specific fields
    context: "0x0x016562aA41A8697720ce0943F003141f5dEAe0060000771577157715"
  }
]
```

この例は、DAppが「**自分の要求どおり**」と決め打ちせず、ウォレットが返した `expiry` や `allowance` をそのまま採用すべきことをわかりやすく示しています。

### `wallet_revokePermissions`

**ERC7715**は、付与された権限を取り消すためのメソッド `wallet_revokePermissions` も定義します。
DAppがこのメソッドを呼ぶと、ウォレットは成功時に空のレスポンスを返します。

#### リクエスト仕様

`params` は以下の形です。

```ts
type RevokePermissionsRequestParams = {
  permissionContext: "0x{string}";
};
```

`permissionContext` は、`wallet_grantPermissions` のレスポンスで返ってきた `context` を渡す用途です。
つまり「**どの権限を取り消すか**」を `context` で指定します。

#### レスポンス仕様

成功時の `result` は空です。

```ts
type RevokePermissionsResponseResult = {};
```

空レスポンスが「**成功**」を意味する設計なので、DApp側は「**例外が返ってこない＝成功**」として扱う実装になることが多いです。

### 全体の流れ

理解しやすいように、付与と取り消しの流れを図にします。

```mermaid
sequenceDiagram
  participant DApp
  participant Wallet
  participant Chain as Blockchain

  DApp->>Wallet: wallet_grantPermissions(params: PermissionRequest[])
  Wallet-->>DApp: PermissionResponse[] (context, 返却値は変更され得る)

  alt accountMeta が返った（未デプロイ）
    DApp->>Chain: factory(factoryData) を呼び出してアカウントをデプロイ
  end

  Note over DApp,Wallet: DApp は以降、返ってきた expiry/permissions を正として扱う

  DApp->>Wallet: wallet_revokePermissions({permissionContext: context})
  Wallet-->>DApp: {}（空レスポンス）
```

### SignerとPermissionのタイプについて

**ERC7715**は、DAppがウォレットに「**どの主体（Signer）が**」、「**何をしてよいか（Permission）**」を伝えるために、よく使われそうな `signer` / `permission` のタイプをいくつか定義しています。

ただし、ここで定義されているのは「**よく使う想定の代表例**」で、網羅リストではありません。
ウォレットが進化すれば新しいタイプが増える前提なので、DAppとウォレットの両方が対応するなら、そのタイプは有効です。

一方で、同じ `type` 名が別物を指してしまうと事故が起きます。
例えば、DAppは「**Aの意味で `type: "key"`**」を要求したのに、ウォレットが「**Bの意味の `type: "key"`**」として許可してしまう、というズレが起こりえます。
これを避けるために、`signer` / `permission` の `type` 名は重複しないことが重要で、新しいタイプは**ERC7715**の改訂として、または別のERCとして仕様化するべきだとしています。

#### Signers

Signerは「**この権限を使って実行する主体は誰か**」を表します。
**ERC7715**では、代表的に以下の4種類を定義しています。

##### 型定義

```ts
// A wallet is the signer for these permissions
// `data` is not necessary for this signer type as the wallet is both the signer and grantor of these permissions
type WalletSigner = {
  type: "wallet";
  data: {};
};

// The types of keys that are supported for the following `key` and `keys` signer types.
type KeyType = "secp256r1" | "secp256k1" | "ed25519" | "schnorr";

// A signer representing a single key.
// "Key" types are explicitly secp256r1 (p256) or secp256k1, and the public keys are hex-encoded.
type KeySigner = {
  type: "key";
  data: {
    type: KeyType;
    publicKey: `0x${string}`;
  };
};

// A signer representing a multisig signer.
// Each element of `publicKeys` are all explicitly the same `KeyType`, and the public keys are hex-encoded.
type MultiKeySigner = {
  type: "keys";
  data: {
    keys: {
      type: KeyType;
      publicKey: `0x${string}`;
    }[];
  };
};

// An account that can be granted with permissions as in ERC-7710.
type AccountSigner = {
  type: "account";
  data: {
    address: `0x${string}`;
  };
};
```

| 署名者タイプ  | `type`  | `data` の中身 | どういう時に使うか    |
| ----------- | ----------- | ------ | ---- |
| ウォレットが署名者   | `"wallet"`  | `{}`（不要）                      | ウォレット自身が「**署名**」と「**権限付与**」をまとめて管理します。DApp側が鍵を持つ必要がなく、ウォレット主導のセッションになります。 |
| 単一鍵   | `"key"`     | `type`（鍵種別）と `publicKey`（公開鍵） | 1本の鍵でセッションを表します。公開鍵は `0x` 付き16進です。   |
| 複数鍵（マルチシグ的） | `"keys"`    | 同じ `KeyType` の公開鍵配列           | 複数の鍵で signer を表します。配列の各要素は同一の鍵種別である必要があります。    |
| アカウント | `"account"` | `address`（アカウントアドレス） | **ERC7710** の委任（delegation）として権限を扱いたいときに使います。 |

補足ですが、`KeyType` に出てくる用語は以下の意味です。

| 用語                | 説明                                   |
| ----------------- | ------------------------------------ |
| `secp256k1`  | EthereumのEOAが一般に使う楕円曲線鍵です。        |
| `secp256r1`（p256） | WebAuthn / Passkey などでよく見る `p256` 系です。 |
| `ed25519` | EdDSA系の代表で、別のチェーンやシステムでもよく使われます。    |
| `schnorr` | Schnorr署名方式を指します（曲線や詳細はここでは固定しません）。 |

#### Permissions

Permissionは「**このセッション（または `signer`）がユーザーの代わりに何をしてよいか**」を表します。
**ERC7715**は代表例として、送金系とセッション消費量を制限する系を定義しています。

##### 型定義

```ts
// Native token transfer, e.g. ETH on Ethereum
type NativeTokenTransferPermission = {
  type: "native-token-transfer";
  data: {
    allowance: "0x..."; // hex value
  };
};

// ERC20 token transfer
type ERC20TokenTransferPermission = {
  type: "erc20-token-transfer";
  data: {
    address: "0x..."; // erc20 contract
    allowance: "0x..."; // hex value
  };
};

// ERC721 token transfer
type ERC721TokenTransferPermission = {
  type: "erc721-token-transfer";
  data: {
    address: "0x..."; // erc721 contract
    tokenIds: "0x..."[]; // hex value array
  };
};

// ERC1155 token transfer
type ERC1155TokenTransferPermission = {
  type: "erc1155-token-transfer";
  data: {
    address: "0x..."; // erc1155 contract
    allowances: {
      [tokenId: string]: "0x..."; // hex value
    };
  };
};

// The maximum gas limit spent in the session in total
type GasLimitPermission = {
  type: "gas-limit";
  data: {
    limit: "0x..."; // hex value
  };
};

// The number of calls the session can make in total
type CallLimitPermission = {
  type: "call-limit";
  data: {
    count: number;
  };
};

// The number of calls the session can make during each interval
type RateLimitPermission = {
  type: "rate-limit";
  data: {
    count: number; // the number of times during each interval
    interval: number; // in seconds
  };
};
```

| 権限タイプ  | `type`  | 何を許すか  | `data` の意味         |
| ----- | --------- | ----------- | ---- |
| ネイティブトークン送金  | `"native-token-transfer"`  | 例：EthereumのETHのようなネイティブ通貨の送金 | `allowance` は 16進の上限値です。                                          |
| **ERC20** トークン送金   | `"erc20-token-transfer"`   | 特定の **ERC20** トークンの送金  | `address` は**ERC20**コントラクト、`allowance` は上限値です。                  |
| **ERC721** トークン送金  | `"erc721-token-transfer"`  | 特定の**ERC721**NFTの送付          | `address` は **ERC721** コントラクト、`tokenIds` は許可するトークンID配列です。    |
| **ERC1155** トークン送金 | `"erc1155-token-transfer"` | 特定の**ERC1155**の送付             | `address` は**ERC1155**コントラクト、`allowances` は tokenId ごとの上限マップです。 |
| ガス上限               | `"gas-limit"`  | セッション全体で消費してよいガスの総量を制限          | `limit` は 16進の上限値です。     |
| 呼び出し回数上限           | `"call-limit"`   | セッション全体で許される呼び出し回数を制限           | `count` が回数です。     |
| レート制限  | `"rate-limit"`  | 一定間隔ごとに許される呼び出し回数を制限  | `interval`（秒）あたり `count` 回まで、という意味です。      |

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

[\[ERC20\] ERC20を1からわかりやすく解説](/articles/aeefe2d89dd5332c9f43)

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

[\[ERC721\] ERC721を1からわかりやすく解説](/articles/0570a8d05bc7c9b9593f)

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

[\[ERC1155\] ERC1155を1からわかりやすく解説](/articles/c3ef92722b057161bebd)

#### ウォレット管理セッション

Signerが `type: "wallet"` の場合は、ウォレット自身がセッションを管理します。

このとき**ERC7715**は、ウォレットがリクエストを承認したなら、**ERC5792**の `wallet_sendCalls` を「**permissions capability付き**」で受け付けなければならないとしています。
さらに、その `capability` に `permissionsContext` を含めてもよいとしています。

`wallet_sendCalls` は「**複数のコールをまとめて送る**」ための呼び出しで、`capabilities.permissions.context` を渡すことで「**このセッション権限で実行してほしい**」を示します。

##### 例

```js
[
  {
    version: "1.0",
    chainId: "0x01",
    from: "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
    calls: [
      {
        to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        value: "0x9184e72a",
        data: "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
      },
      {
        to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
        value: "0x182183",
        data: "0xfbadbaf01",
      },
    ],
    capabilities: {
      permissions: {
        context: "<permissionContext>",
      },
    },
  },
];
```

##### ウォレット側の期待動作

このリクエストを受け取ったウォレットは、要求された `permissions` に従って `call` を送信する必要があります。
そして、追加のトランザクション確認をユーザーに求めるべきではありません。

ここでの `permissionsContext` は、前の節で出てきた `wallet_grantPermissions` のレスポンス内 `context` と同種の「**権限の識別子**」として扱われています。

理解用に流れを図にすると以下です。

```mermaid
sequenceDiagram
  participant DApp
  participant Wallet
  participant Chain as Blockchain

  DApp->>Wallet: wallet_grantPermissions（signer.type="wallet"）
  Wallet-->>DApp: context / permissionsContext を返す

  DApp->>Wallet: wallet_sendCalls（capabilities.permissions.context を付与）
  Wallet->>Chain: permissions に従って calls を送信（MUST）
  Note over Wallet: 追加の確認は求めない（SHOULD NOT）
```

#### Capabilities

ウォレットが**ERC5792**をサポートする場合、`wallet_getCapabilities` に対して `permissions` キーを使って応答することが推奨されています。

さらに、ウォレットが対応している signer / permission のタイプを、レスポンス内に含めることが推奨されています（**SHOULD**）。これにより、DApp は「このウォレットは何ができるか」を事前に判断できます。

##### `wallet_getCapabilities` の例

```json
{
  "0x123": {
    "permissions": {
      "supported": true,
      "signerTypes": ["keys", "account"],
      "keyTypes": ["secp256k1", "secp256r1"],
      "permissionTypes": ["erc20-token-transfer", "erc721-token-transfer"]
    }
  }
}
```

ここではチェーンごと（例：`"0x123"`）に、`permissions` がサポートされているか、どの `signerTypes` / `permissionTypes` / `keyTypes` に対応しているかを返しています。

##### CAIP-25を使う場合

ウォレットが**CAIP25 authorization**を使っている場合、`sessionProperties` に `permissions` キーを含めることが推奨されています。加えて、対応タイプをカンマ区切り文字列で入れる追加キーも推奨されています。

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

[\[ACP25\] リクエスト失敗を即時通知するAppErrorの仕組みを理解しよう！](/articles/86dbeeea0e0ac179db98)

**CAIP25**は「**ウォレットセッションの権限や性質をメタデータとして表現する**」ための枠組み、と捉えると読みやすいです。

例は以下です。

```json
{
  "sessionProperties": {
    "permissions": "true",
    "signerTypes": "keys,account",
    "permissionTypes": "erc20-token-transfer,erc721-token-transfer"
  }
}
```

#### セッションを使ったトランザクション送信

ここからは、「**権限を付与してもらったあと実際にどう送るか**」の話です。**ERC7715**は大きく2ルートを示しています。

* **ERC7679**
    `key` / `keys` signer を使うルート（**ERC4337** の `userOp` を組み立てる）
* **ERC7710**
    `account` signerを使うルート（委任を `redeem` して実行する）

#### ERC7679

`signer.type` が `key` または `keys` のとき、`wallet_grantPermissions` のレスポンスには以下が返る想定です。

* `permissionsContext`（権限コンテキスト。前段の `context` と同じ役割として扱われています）
* `signerMeta.userOpBuilder`（UserOp Builderのコントラクトアドレス）

DAppはこれらを使って、**ERC7679**が提供する方法で**ERC4337**の `userOp` を構築します。

また **ERC7679**のUserOp Builderコントラクトは、すべてのメソッドに `bytes calldata context` を持ちます。
これは `wallet_grantPermissions` で返った `permissionsContext` と同等だと説明されています。

##### `userOp` 署名フォーマット例

```js
const getSignature = async ({
  address,
  userOp,
  permissionsContext,
}: GetSignatureArgs) => {
  return readContract(config, {
    abi: BUILDER_CONTRACT_ABI,
    address: BUILDER_CONTRACT_ADDRESS,
    functionName: "getSignature",
    args: [address, userOp, permissionsContext],
  });
};
```

この例では、**Builder**コントラクトの `getSignature` に `permissionsContext` を渡して、セッション権限に紐づく署名を作っています。

![erc7679-flow.png](/images/qiita-12145209-196a-47b6-abca-3571c081cde5.webp)
https://eips.ethereum.org/EIPS/eip-7715#signer--permission-types

```mermaid
flowchart LR
  A[DApp: wallet_grantPermissions] --> B[Wallet: permissionsContext と userOpBuilder を返す]
  B --> C[DApp: userOp を作る]
  C --> D[DApp: userOpBuilder に context を渡して署名を作る]
  D --> E[UserOp を送る（ERC-4337）]
```

#### ERC-7710

`signer.type` が `account` のとき、返ってきたデータは**ERC7710**のインターフェースで `redeem`（償還）できる形になっています。

やりたいことはシンプルで、「**権限を受け取った側が、ユーザーのアカウントに このメッセージを発行して実行してと依頼する**」です。
送金でも、`userOp` でも、支払い／リレーのインフラは受け取り側が好きに選べる、と説明されています。

具体的には、`permissions.signerMeta.delegationManager` に対して、以下の関数を呼び出します。

* `redeemDelegation(bytes calldata _data, Action calldata _action)`

ここで `_data` は `permissions.permissionsContext` を渡します。
`_action` は「**ユーザーアカウントにやらせたい処理**」を表します。

##### `Action` 構造体

```js
struct Action {
    address to;
    uint256 value;
    bytes data;
}
```

`to` は呼び出し先、`value` は送金額、`data` は calldata です。

##### 疑似コード例

```js
// Alice requests a permission from Bob
const permissionsResponse = await bob.request({
  method: 'wallet_grantPermissions',
  params: [{
    address: bob.address,
    chainId: 123,
    signer: {
      type: 'account',
      data: {
        id: alice.address
      }
    },
    permissions: [
      {
        type: 'native-token-transfer',
        data: {
          allowance: '0x0DE0B6B3A7640000'
        },
      },
      {
        type: 'gas-limit';
        data: {
          limit: '0x0186A0',
        },
      },
    ],
    expiry: Math.floor(Date.now() / 1000) + 3600 // 1 hour from now
  }]
});

// Extract the permissionsContext and delegationManager
const permissionsContext = permissionsResponse.permissionsContext;
const delegationManager = permissionsResponse.signerMeta.delegationManager;

// Alice forms the action she wants Bob's account to take
const action = {
  to: alice.address,
  value: '0x06F05B59D3B20000'
  data: '0x'
};

// Alice sends the transaction by calling redeemDelegation on Bob's account
const tx = await bob.sendTransaction({
  to: delegationManager,
  data: bob.interface.encodeFunctionData('redeemDelegation', [
    permissionsContext,
    action
  ])
});
```

この例は「**AliceがBobから `permission` をもらい、Bobのアカウントに自分（Alice）宛の送金をさせる**」という流れです。
`permission` の中に `native-token-transfer` と `gas-limit` があり、さらに `expiry` で 1時間に制限されています。

なお、Signer の型定義では `data` が `{ address: ... }` ですが、この例では `id` になっています。
ここは、実装時に「**どのキー名で何を渡すか**」を仕様側と揃える必要があるポイントです（原文内で表記がずれています）。

理解用に流れを図にすると以下です。

```mermaid
sequenceDiagram
  participant Alice
  participant BobWallet as BobのWallet/Provider
  participant DelegationManager
  participant Chain as Blockchain

  Alice->>BobWallet: wallet_grantPermissions（signer.type="account"）
  BobWallet-->>Alice: permissionsContext と signerMeta.delegationManager を返す

  Alice->>DelegationManager: redeemDelegation(permissionsContext, Action)
  DelegationManager->>Chain: Bobのアカウントが Action を実行
```

## 補足

**ERC7715**が狙っているのは、これまで一般的だった「**トランザクションを提案する → 承認する → 送信する**」という流れが持つ限界を避けることです。
この流れは、一見わかりやすいのですが、DAppの設計とユーザー体験の両方を強く縛ります。

まず、ユーザーがオンラインでないと送信できない点が大きな制約です。
ウォレットが接続されておらず、ユーザーがその場にいないと、DAppはユーザーの代わりにトランザクションを出せません。
その結果、サブスクリプションのような定期実行や、自動売買のような条件付き実行が成立しません。

次に、すべての操作で毎回ユーザー承認が必要になる点です。
ゲームのように操作回数が多い DAppでは、ウォレットのポップアップと承認が頻繁に入り、体験が途切れます。
「**本当は滑らかに進めたい操作**」が「**署名作業の連続**」になってしまいます。

**ERC7715**は、DAppがウォレットに対して権限付与をリクエストできるようにし、ウォレットが許可した範囲内でDAppがユーザーの代わりに実行できるようにします。
これによって、オンライン必須と毎回承認必須という2つの問題を回避できるようにしています。

#### `permissionsContext`

**ERC7715**は「**DAppとウォレットのやり取り**」を標準化しますが、「**ウォレットが権限をどう強制するか**」までは規定しません。
ウォレットによって権限の実現方法が違ってもよい、という設計です。

ただ、そのままだとDApp側が困ります。
DAppは、ウォレットが許可した権限を反映した形でトランザクションや `userOp` を組み立てる必要があります。
しかし、権限の実現方式がウォレットごとに違うなら、DAppに渡す「**手がかり**」が必要です。

そこで **ERC7715**は `permissionsContext`を用意します。

##### `permissionsContext` の性質

`permissionsContext` は「**中身をDAppが解釈できるとは限らない不透明な文字列**」です。
つまり、DAppにとっては「**意味を決め打ちせず、ウォレットが渡した値をそのまま別の呼び出しに渡すもの**」として扱うのが基本です。

この不透明さが重要で、ウォレットは権限方式に応じて自由に情報を詰められます。**ERC7715**は、その利用先として特に3つの方式を想定しています。

##### 想定される3つの使い方

| 想定スキーム  | DAppが `permissionsContext` を使う場所             | どういう意味で使うか     |
| ----------- | --------------- | -------------- |
| **ERC7679**を使う  | UserOp Builderとやり取りする `context` 引数         | `userOp` を組み立てる時に「**この権限セッションである**」ことを埋め込むためのコンテキストになります。  |
| **ERC7710**を使う  | delegation managerの `_data` 引数 | `redeemDelegation` などで委任を償還するためのデータとして、そのまま渡します。     |
| アプリ内セッション（in-app sessions）を使う | **ERC5792**の `wallet_sendCalls` のセッション識別子 | `wallet_sendCalls` 実行時に「**どのセッション権限でやるか**」を示す識別子として扱います。 |

「**アプリ内セッション**」は、DAppがアプリ側でセッション概念を持ち、ウォレットに対しては「**このセッションで送って**」という合図だけ渡す形を指しています。

理解用に、`permissionsContext` の流れを図にします。

```mermaid
flowchart TB
  A[DApp: wallet_grantPermissions] --> B[Wallet: permissionsContext を返す]

  B --> C1[ERC-7679: UserOp Builder の context に渡す]
  B --> C2[ERC-7710: delegationManager の _data に渡す]
  B --> C3[ERC-5792: wallet_sendCalls の session識別子として渡す]
```

#### SignerとPermissionの非網羅リスト

**ERC7715**が `signer` / `permission` の型を網羅しないのは、ウォレット技術の進化で新しい型が増える前提だからです。
つまり、将来的に「**こういう権限が欲しい**」、「**こういう署名主体が必要**」という要求が必ず出てくるので、最初から閉じたリストにしない方が現実的です。

ただし前提として、DAppとウォレットの両方がその型をサポートする意思があることが必要です。
片方だけが知っている型では動きません。

##### 衝突回避についての考え方

型名の衝突を避けるために「**UUIDを必須にする**」案も検討したと書かれています。
UUIDにすると、型名の重複が起きにくいからです。

しかし **ERC7715**は最終的に、より単純な方針を選びました。
つまり「**`signer` / `permission` の `type` はERCで定義することを必須にする**」というやり方です。
ERCとして公開されれば、型名の重複を避けやすく、実装者も参照しやすいという判断です。

## 互換性

**ERC7715**に対応していないウォレットが `wallet_grantPermissions` を呼ばれた場合、エラーを返すことが推奨されています。

つまりDApp側の現実的な挙動としては、`wallet_grantPermissions` を試し、エラーが返ったら従来フロー（毎回署名）にフォールバックする、という形になります。

ここは「**未対応でも黙って無視しないで、明確に失敗として返してほしい**」という期待です。
DAppが「**対応していない**」ことを判定しやすくなります。

## セキュリティ

**ERC7715**は「**ユーザーの代わりに実行できる**」仕組みなので、設計を間違えると権限が強すぎて危険になります。

#### 権限スコープの制限

DAppは、必要な権限だけを要求し、妥当な期限を設定するべきです。
これは「**何でもできる長期権限**」を避けるという話です。

また、ウォレットは権限を正しく強制する必要があります。
最終的にはユーザーは自分のウォレット実装を信頼するしかなく、権限 `enforcement` はウォレット実装の一部として扱われます。

ここは責任分界がはっきりしていて、DAppがいくら「**制限したつもり**」でも、ウォレットが `enforcement` を誤れば意味がありません。
逆に、ウォレットが正しく `enforcement` するなら、DAppが要求した範囲以上のことは起きにくくなります。

権限スコープの考え方を整理すると以下です。

| 観点     | 期待されること |
| ------ | ------------ |
| DApp側 | 必要最小限の `permission` を要求し、短すぎず長すぎない expiry を付けます。           |
| ウォレット側 | 許可した範囲を正しく守らせます。権限 `enforcement` はウォレット品質そのものです。 |

##### フィッシング攻撃

悪意あるDApp が、正規アプリのふりをしてユーザーに広い権限を与えさせる可能性があります。
これは「**署名を騙し取る**」よりも強烈で、広い権限を一度与えるとその範囲内では継続して悪用されうるからです。

そのため **ERC7715**は、ウォレットがユーザーに対して権限内容を明確に表示し、危険な権限に対して警告を出さなければならないとしています。

ここでの「**明確に表示**」とは、少なくとも以下がユーザーに伝わる状態を指すと考えるのが自然です。

* 何が許可されるのか（例：送金、特定トークン移転、回数制限など）
* どの範囲か（上限、対象コントラクト、対象 tokenId など）
* いつまで有効か（expiry）

## 引用

Luka Isailovic (@lukaisailovic), Derek Rein (@arein), Dan Finlay (@danfinlay), Derek Chiang (@derekchiang), Fil Makarov (@filmakarov), Pedro Gomes (@pedrouid), Conner Swenberg (@ilikesymmetry), Lukas Rosario (@lukasrosario), "ERC-7715: Grant Permissions from Wallets [DRAFT]," Ethereum Improvement Proposals, no. 7715, May 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7715.

## 最後に

今回は「ウォレットがDAppに対して実行権限を付与し、ユーザーの代わりにトランザクションを送信できる仕組みを提案している**ERC7715**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

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