はじめに
『DApps開発入門』という本や色々記事を書いているかるでねです。
今回は、ユニバーサルウォレットが自分の配下にある別のスマートアカウントを作成したり追跡したりできるようにするRPCを定義するERC7895についてまとめていきます!
以下にまとめられているものを解説しながらまとめていきます。
他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。
概要
ERC7895は、アプリがウォレットに対して「このアカウントをあなたの配下アカウントとして扱ってほしい」と要求するためのRPCである wallet_addSubAccount を定義する提案です。
ここでいう account はEOAではなく、基本的にはスマートアカウントを指します。
提案の狙いは、アプリごとに作られた埋め込み型アカウントと、ユーザーが普段使うユニバーサルウォレットを階層的につなぐことです。
たとえばゲームやDeFiアプリが、ユーザーごとに専用アカウントを持っていたとしても、そのアカウントの所有者にユニバーサルウォレットを追加できれば、ユーザーはアプリ外からも資産や権限を把握しやすくなります。
アプリ側は新しいサブアカウントの作成を依頼することもできますし、すでに存在するアカウントをウォレットへ取り込んでもらうこともできます。
この提案はEIP5792をベースにしています。
EIP5792については以下の記事を参考にしてください。
以下の図は、ERC7895が扱う全体像です。
この提案がしていることは、アプリ専用アカウントをユニバーサルウォレットの配下へぶら下げるための標準的な窓口を作ることです。
実行ロジックを増やすというより、ウォレットが「どのアカウントを自分の管理対象として追うか」を共有するための規格です。
動機
埋め込み型ウォレットやアプリ専用アカウントは、ユーザー体験の面では便利です。
一方で、アプリごとに別アドレスが増えるため、ユーザー目線では「自分がどのアカウントを持っているのか」が分かりにくくなります。
特に、すでにユニバーサルウォレットを使っているユーザーにとっては、各アプリの専用アカウントがウォレットの外側に散らばる状態になります。
これでは、資産管理、権限管理、復旧導線の設計がばらけやすくなります。
以下の図は、アプリごとにアカウントが散らばる状態と、親ウォレット配下へまとめる状態の違いです。
ここで見たいのは、すべてのアカウントを1つに統合することではなく、管理の入口だけを親ウォレットへ寄せるという点です。
ゲーム用、DeFi用、アプリ専用といった役割の違いは残したまま、追跡や権限確認の起点をそろえられます。
提案が注目しているのは、スマートアカウント同士の階層的な所有です。
あるスマートアカウントAが別のスマートアカウントBの所有者になれるなら、BはAのサブアカウントとして振る舞えます。
すると、アプリ専用アカウントをユーザーのユニバーサルウォレット配下へぶら下げる設計が可能になります。
ただし、既存の仕組みだけでは、アプリが「このアカウントはあなたの配下です」とウォレットへ標準的に伝える手段がありません。
この隙間を埋めるのがERC7895です。
要するにこの提案は、アカウント抽象化そのものを定義する規格ではなく、アプリとウォレットのあいだで階層アカウント関係を共有するための窓口を定義しています。
そのため論点は、署名アルゴリズムや実行モデルよりも、「どのアカウントをどう配下へ追加するか」に寄っています。
仕様
仕様の主役は wallet_addSubAccount です。
そのあとに、wallet_connect と wallet_getCapabilities で同じ考え方を外部ケーパビリティとして扱う方法が続きます。
前提となる用語
提案では、account をスマートアカウントとして扱います。
そしてサブアカウントは、メインアカウントを所有者の1人として持つべきアカウントです。
つまり「AがBのサブアカウントである」とは、AがBを操作できるという意味ではなく、Bの所有者集合の中にAが含まれているという関係です。
これは親子関係というより、所有権の階層です。
wallet_addSubAccount
wallet_addSubAccount は、接続中のユニバーサルウォレットに対して、アプリ側のアカウントを追跡対象に追加したり、新しいサブアカウントを作らせたりするRPCです。
提案では、次の3つのユースケースを1つのRPCにまとめています。
| 種類 | 何をしたいか | 主な入力 |
|---|---|---|
create |
新しいサブアカウントをウォレットに作ってほしい | keys |
deployed |
すでに存在するアカウントをウォレットに取り込んでほしい | address |
undeployed |
まだデプロイしていないカウンターファクトリアカウントを追跡してほしい |
address factory factoryData
|
以下の図は、この3つの違いを先に整理したものです。
この比較で見たいのは、目標がどれも「サブアカウントを親ウォレット配下へ置く」ことで共通していても、ウォレットが受け取る情報と検証の仕方が同じではない点です。
特に undeployed は、まだ実体のないアカウントを扱うため、所有関係の検証が他の2つより重くなります。
リクエスト全体の形は以下です。
type Request = {
method: "wallet_addSubAccount";
params: [{
version: string;
account: CreateAccount | DeployedAccount | UndeployedAccount;
}];
}
ここでの version は、このJSON-RPCメソッド自体のバージョンです。
account には、3種類のいずれかの定義を入れます。
CreateAccount
CreateAccount は、新しいサブアカウントの作成をウォレットへ依頼する形です。
特徴は、アプリ側がアカウント実装の細部を固定しなくても、「この署名鍵を所有者として持つサブアカウントを用意してほしい」と要求できる点です。
type CreateAccount = {
type: "create";
keys: {
publicKey: "0x...";
type: "address" | "p256" | "webcrypto-p256" | "webauthn-p256";
}[];
}
鍵を省略した場合、ウォレットはサブアカウント用の署名鍵を自分で作成し、管理することが想定されています。
一方で、アプリが keys を渡せば、その鍵を所有者集合へ含める形のサブアカウントを作る余地があります。
重要なのは、ウォレットがただ新規アカウントを作るだけでなく、ユニバーサルアカウントをその所有者に含めるべきだとしている点です。
この条件がないと、作っただけで階層関係ができません。
DeployedAccount
DeployedAccount は、すでにオンチェーンに存在するアカウントをウォレットへ登録する形です。
いわばインポートに近い使い方です。
type DeployedAccount = {
type: "deployed";
address: `0x${string}`;
keys: never;
chainId?: `0x${string}`;
factory?: never;
factoryData?: never;
}
このモードでは、アプリやユーザーがすでに持っているスマートアカウントを、ユニバーサルウォレット側の管理対象へ加えることが主目的です。
たとえば、アプリ内で以前から使っていたアカウントを、あとからメインウォレット配下へ認識させたい時に向いています。
UndeployedAccount
UndeployedAccount は、アプリ側がまだデプロイしていないアカウントを先に設計していて、そのカウンターファクトリアドレスをウォレットに覚えさせたい時に使います。
type UndeployedAccount = {
type: "undeployed";
address: `0x${string}`;
keys: never;
chainId?: `0x${string}`;
factory: `0x${string}`;
factoryData: `0x${string}`;
}
この形では、factory と factoryData が重要です。
ウォレットは、必要になった時にそのアカウントを本当にデプロイしてよいかを判断しなければなりません。
提案では、ウォレットはユニバーサルアカウントがそのサブアカウントの所有者であることを検証すべきだとしています。
検証方法としては、factoryData をデコードする方法と、デプロイをシミュレーションする方法の2つが挙げられています。
以下の図は、undeployed モードでウォレットが何を確かめるのかを整理したものです。
ここで大事なのは、まだ実体のないアカウントだからこそ、ウォレットが「あとで作られるはずのアカウント」を無条件で受け入れてはいけないことです。
address、factory、factoryData を受け取ったうえで、親ウォレットが所有者になる設計かどうかを確認してから追跡対象へ入れる必要があります。
これはかなり大事な条件です。
単にアプリが任意アドレスを渡せるだけだと、ウォレットが自分と無関係なアカウントを誤って追跡する危険があります。
レスポンス
レスポンスでは、少なくともアカウントアドレスを返します。
factory と factoryData は、分かる場合に返してよいオプションです。
type Response = {
address: `0x${string}`;
factory?: `0x${string}`;
factoryData?: `0x${string}`;
}
ここで見たいのは、ウォレットが最終的にどのアカウントを追跡対象として認識したかです。
特に create モードでは、アプリが事前にアドレスを知らない場合もあるため、この戻り値が実装上の基準点になります。
wallet_connect の外部ケーパビリティ
提案の後半では、同じ考え方を wallet_connect のケーパビリティとして扱う方法が定義されています。
追加されるのは addSubAccount と subAccounts の2つです。
addSubAccount
addSubAccount は、wallet_connect の要求側から、サブアカウント追加の意思をケーパビリティとして添える形です。
type Request = {
addSubAccount: {
account: CreateAccount | DeployedAccount | UndeployedAccount;
}
}
ここでの意味は、wallet_addSubAccount を完全に置き換えることではありません。
接続フローの中で「このアカウントも関係づけたい」という要求を一緒に伝えられるようにすることです。
subAccounts
subAccounts は、ユニバーサルアカウントに紐づくサブアカウント一覧を返すためのケーパビリティです。
type Response = {
subAccounts: {
address: `0x${string}`;
factory?: `0x${string}`;
factoryData?: `0x${string}`;
}[];
}
これにより、アプリは「今このウォレット配下にどんなサブアカウントがあるか」をまとまって受け取れます。
新規追加だけでなく、既存の関係を把握する導線も用意されているわけです。
wallet_getCapabilities
ウォレットがこの機能に対応しているかを調べるために、wallet_getCapabilities で addSubAccount サポートを返せるようにしています。
type Response = {
addSubAccount: {
supported: true;
keyTypes: ("address" | "p256" | "webcrypto-p256" | "webauthn-p256")[];
}
}
ここで重要なのは、単に対応の有無だけでなく、どの鍵タイプを受け付けるかまで表明できる点です。
アプリはこれを見て、どの種類のサブアカウント作成要求を出せるかを判断できます。
たとえば webauthn-p256 に対応していないウォレットへ、その前提の create 要求を投げるべきではありません。
ケーパビリティ問い合わせを先に行うことで、ウォレットごとの差異を吸収しやすくなります。
以下の図は、wallet_getCapabilities から追加要求、追跡、再取得までの流れをまとめたものです。
この流れで重要なのは、wallet_addSubAccount が単独で浮いているわけではなく、ケーパビリティ照会と一覧取得の導線に挟まれている点です。
そのため実装時は、追加APIだけでなく、「事前確認」と「事後に追えること」までセットで見る必要があります。
補足
3つのモードの役割分担
3つのモードは似ていますが、実際には役割がかなり違います。
| モード | 主役 | 典型的な状況 |
|---|---|---|
create |
ウォレット | サブアカウント自体を今から作りたい |
deployed |
既存アカウント | すでにあるアカウントを親ウォレットへ関連づけたい |
undeployed |
アプリ側の設計済みアカウント | 先にアドレスだけ作り、必要時にデプロイしたい |
この切り分けがあるおかげで、アプリは自分の設計に応じて最小限の情報だけを渡せます。
ウォレット側も、すべてを同じロジックで処理するのではなく、「新規作成なのか」、「既存取込なのか」、「デプロイ前追跡なのか」を分けて扱えます。
典型的な利用フロー
アプリ統合の流れは以下です。
-
wallet_getCapabilitiesでaddSubAccount対応とkeyTypesを確認する - 必要に応じて
create、deployed、undeployedのどれかでwallet_addSubAccountを呼ぶ - ウォレットが所有関係を検証し、追跡対象として登録する
- 以後は
wallet_connectやsubAccounts経由で一覧管理や接続導線へつなぐ
この流れから分かるように、ERC7895は単なるインポートAPIではありません。
ケーパビリティ照会、所有関係の検証、接続導線まで含めて、アプリアカウントをユニバーサルウォレットの世界へ取り込むための規格です。
提案本文の読み取りで気を付けたい点
UndeployedAccount については、仕様前半の型定義では factory と factoryData が必須として示されています。
一方で、その後ろの説明ブロックには省略形の記述が混ざっており、見た目だけ追うと食い違って見えます。
ここは仕様本文全体からの読み取りですが、未デプロイアカウントの所有関係を検証する説明が factoryData やデプロイシミュレーションを前提にしているため、実装解釈としては前半の型定義を正と見るのが自然です。
命名の意図
Rationaleでは、wallet_linkAccount、wallet_importAddress、wallet_addAddress のような名前も検討されたと書かれています。
最終的に wallet_addSubAccount が選ばれたのは、既存アカウントの取込と新規作成の両方を1つの名前で包みやすいからです。
つまりこの名前は、「ただアドレスを追加する」のではなく、階層関係を持つサブアカウントとして扱うことを強調しています。
互換性
ERC7895は、既存のJSON-RPCを壊して置き換える提案ではありません。
新しいRPCとケーパビリティを追加し、ウォレットが段階的に対応できるようにしています。
そのため、未対応ウォレットは従来どおり動かせます。
対応ウォレットだけが、アプリ専用アカウントの追跡や作成を標準化された形で提供できます。
また、後半のケーパビリティ部分はEIP5792の拡張点を利用しています。
このため、すでにWallet Call API系の仕組みに乗っているウォレットほど取り込みやすい構成です。
セキュリティ
この提案のセキュリティ上の中心は、本当に自分の配下に置いてよいアカウントだけをウォレットが受け入れることです。
特に undeployed モードでは、まだ実体のないアカウントを扱います。
ウォレットが factoryData の意味を十分に検証しないまま受け入れると、所有関係がないアカウントを誤って関連づける危険があります。
また、アプリ専用アカウントは資産や権限を持つことがあります。
そのためウォレットがサブアカウントを一覧表示したり、ケーパビリティ情報を返したりする時は、漏れてよい情報とそうでない情報を分ける必要があります。
提案本文でも、データ漏えいや中間者攻撃を避けるため、安全な取り扱いが必要だとしています。
もう1つ大事なのは、アプリ専用アカウントをユニバーサルウォレット配下に置くこと自体が、利便性とリスクの両方を増やす点です。
一元管理しやすくなる一方で、親ウォレットが持つ影響範囲も広がります。
実装側は、表示、確認UI、承認フローを雑にせず、「どのアプリのどのアカウントを配下へ追加するのか」がユーザーに分かるようにする必要があります。
最後に
今回は、ユニバーサルウォレットがアプリ専用アカウントを自分の配下へ追加したり、新しく作成したりできるようにするERC7895についてまとめてきました!
ERC7895は派手な実行ロジックを増やす提案ではありませんが、アプリごとに散らばりがちなスマートアカウントを、ユーザーのメインウォレットへ再接続するための重要な土台です。
特に、埋め込み型ウォレット、アプリ専用アカウント、スマートアカウント運用が当たり前になるほど、この種の「関係づけAPI」の重要性は上がっていくはずです。
現時点では Draft ですが、ウォレット実装者とdApp開発者の両方にとって、かなり実務寄りの提案だと思います。
今後は、どのスマートアカウント実装を前提にするか、所有関係の検証をどこまで共通化できるか、UIでどう安全に見せるかが実装上の差になっていきそうです。
他でも色々記事を書いているのでぜひよろしければ読んでいってください!




