[ERC2544] ENSでワイルドカードサブドメインの解決を可能にする仕組みを理解しよう！

# はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、ENSでワイルドカードサブドメインの解決を可能にする仕組みを提案している**ERC2544**についてまとめていきます！

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

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

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

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

# 概要

現在のEthereum Name Service（ENS）は、名前を解決する際に2段階の処理を行います。
まず、ENSクライアントがドメイン名に対して`namehash`アルゴリズムを適用し、対応するノード（node）を求めます。
次に、このノードをENSレジストリコントラクトに渡し、関連するリゾルバ（resolver）が設定されているかどうかを確認します。
設定されていれば、そのリゾルバに対して同じノードを渡し、アドレスやその他の情報を取得します。

しかし、リゾルバが設定されていない場合、このプロセスはそこで終了してしまいます。
**ERC2544**では、この動作に変更を加え、リゾルバが設定されていない場合に以下の処理を追加します。

* ドメイン名の最左部のラベル（例: `alice.example.eth` の `alice`）を取り除き、残った部分（例: `example.eth`）のノードを新たに生成する
* そのノードにリゾルバがあるかENSレジストリに問い合わせる
* 見つかれば、元の完全なノードをそのリゾルバに渡し、レコードを解決する

これをリゾルバが見つかるまで繰り返します。

さらに、**ERC2544**では、リゾルバが統一的に動作するための`resolve()`という新しいメソッドも定義しています。
これにより、より柔軟な名前解決が可能になります。

# 動機

現在、多くのウォレットプロバイダや取引所、dAppなどは、ユーザーごとにカスタムサブドメイン（例: `alice.wallet.eth`）を発行したいと考えています。
しかし、そのためには各サブドメインごとに個別にENSレジストリにレコードを設定する必要があり、大規模なユーザーベースに対してはコストが高すぎます。

また、ユーザーがサブドメインを使えるようになるまでに、リゾルバの設定トランザクションをオンチェーンで送信・マイニングしなければならず、新規ユーザーのオンボーディング時に手間が増えてしまいます。

**ERC2544**によって「**ワイルドカード解決**」が可能になると、リゾルバが未設定のサブドメインに対しても、親ドメインのリゾルバが動的にレコードを生成できるようになります。
これにより以下のような機能が実現できます。

* `CREATE2`を用いたコントラクトの予測アドレス（カウンターファクチュアルアドレス）を返す
* 固定のフォールバックアドレスを返す
* その他の動的な解決方法

さらに、**ERC2544**は互換性を保っており、ENSレジストリ自体や既存のリゾルバに変更を加える必要がありません。
従来のENSクライアントはワイルドカード解決に対応していないため、これまでどおりの挙動を維持しつつ、新しいクライアントではワイルドカードをサポートできるようになります。

# 仕様

## 定義

* `namehash`
    * **EIP137**で定義された、ENSで名前をハッシュ化するアルゴリズム。
* `dnsencode`
    * RFC1035のセクション3.1に基づくDNS形式の名前エンコード。
    * ただし、全体の長さ制限はなく、空文字列は `.` としてエンコードされ、1バイトの0オクテットになる。
* `parent(name)`
    * 指定された名前の最初のラベルを取り除いた名前を返す。
    * 例えば、`parent('foo.eth') = 'eth'`。
* `ens`
    * 現在のネットワークにおけるENSレジストリコントラクト。

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

https://qiita.com/cardene/items/17e6e1a452006f08a197

## インターフェース

```js:ExtendedResolver.sol
interface ExtendedResolver {
    function resolve(bytes calldata name, bytes calldata data) external view returns(bytes);
}
```

このインターフェースを実装しているリゾルバは、`supportsInterface()`関数に対してそのインターフェースID（`0xTBD`）を引数に与えたときに `true` を返す必要があります。

## 名前解決の流れ（リゾルバ取得）

**ERC2544**準拠のENSクライアントは、以下の手順でドメイン名に対応するリゾルバを検索します。

1. `currentname = name` として初期化。
2. `ens.resolver(namehash(currentname))` でリゾルバを取得。
3. リゾルバがゼロアドレスでなければそれを返して終了。
4. 名前が空文字列（`''` または `'.'`）であれば `null` を返して終了。
5. それ以外なら `currentname = parent(currentname)` とし、ステップ2に戻る。

この手順でリゾルバが見つからない場合は、名前解決に失敗します。

## レコード解決の流れ

リゾルバが見つかった場合、**ERC2544**準拠のENSクライアントは以下の手順でレコードを解決します。

1. 解決に必要な関数（例えば `addr()`）のABIエンコードされた呼び出しデータ（calldata）を用意する。
2. `resolver.supportsInterface(0xTBD)` を呼び出して、**ERC2544**対応かどうかを確認する。
3. **ERC2544**対応であれば、`resolver.resolve(dnsencode(name), calldata)` を呼び出し、結果をデコードする。
4. 非対応であれば、通常の関数呼び出し（例えば `resolver.addr(...)`）を行う。

**注意点**

リゾルバに渡す関数や引数は、リゾルバが設定された `currentname` ではなく、常に元の完全な `name` に基づく必要があります。

## 擬似コードによる動作説明

```javascript
function getResolver(name) {
    for(let currentname = name; currentname !== ''; currentname = parent(currentname)) {
        const node = namehash(currentname);
        const resolver = ens.resolver(node);
        if(resolver != '0x0000000000000000000000000000000000000000') {
            return resolver;
        }
    }
    return null;
}
```

この関数は、左から順にラベルを取り除いていきながら、リゾルバが設定されているかを確認します。

```javascript
function resolve(name, func, ...args) {
    const resolver = getResolver(name);
    if(resolver === null) {
        return null;
    }
    const supports2544 = resolver.supportsInterface('0xTBD');
    let result;
    if(supports2544) {
        const calldata = resolver[func].encodeFunctionCall(namehash(name), ...args);
        result = resolver.resolve(dnsencode(name), calldata);
        return resolver[func].decodeReturnData(result);
    } else {
        return resolver[func](...args);
    }
}
```

この関数は、対応するリゾルバが**ERC2544**に準拠しているかを判定し、それに応じた方法で名前解決を行います。
**ERC2544**対応であれば `resolve()` メソッドを使用し、非対応であれば通常の関数呼び出しを行います。

# 補足

**ERC2544**は、既存のENSシステムへの影響を最小限に抑えながら、ワイルドカード解決機能を追加することを目的としています。
すでに存在するアルゴリズムや処理（たとえば`namehash`や既存のレジストリ操作）をそのまま利用しており、ENSクライアントの開発者やメンテナが新たに覚えるべきことや実装の負担をできるだけ軽くしています。

ENSのコミュニティでは以前からワイルドカード解決への需要が強く、この仕様はそのニーズを満たす形で導入されます。
特に、スケーラビリティに関する課題を解消することに貢献し、ENS名の普及に弾みをつけるものとなります。

新たに導入される `resolve()` 関数は、名前の平文（未ハッシュの形式）とリゾルバ呼び出しに必要な`calldata`を直接扱うことで、リゾルバが名前の各ラベルを読み取り、柔軟に対応できるようにします。
これにより、例えば `id.nifty.eth` のようなサブドメインから、NFTの所有者情報を動的に取得するようなユースケースが可能になります。
これは、従来の `namehash` のみを使う方式では不可能でした。

一方で、複雑な処理が不要なリゾルバは、これまで通り個別の解決関数（例えば `addr()` など）のみを実装すればよく、`resolve()` をサポートする必要はありません。
このようにして、柔軟性と実装の簡潔さの両立が図られています。

また、DNSワイヤ形式を用いて名前をエンコードする理由は、ガス効率が良く、名前の各ラベル（部分）を容易に取り出したり変更したりできるからです。
ドット区切りの文字列では、区切り文字（`.`）の位置を探すためにすべての文字を走査する必要があり、効率が悪いです。

# 互換性

既存のENSクライアント（**EIP137**に準拠しているもの）は、ワイルドカード対応が実装されていないため、ワイルドカードレコードの解決には失敗します。
ただし、**ERC2544**に準拠したクライアントであれば、これまでのENSレコードにも対応しつつ、新たなワイルドカード解決も処理できます。

リゾルバ側で `resolve()` を非ワイルドカード用途（例えばドメイン名に直接設定されたレコードの解決）にも使いたい場合は、従来の個別解決関数（`addr()`など）を呼び出す古いクライアントに対しても適切に応答できるように、互換性を意識した設計が望まれます。
これにより、古いクライアントでもエラーが出ず、より広範囲で利用できるリゾルバとなります。

# セキュリティ

## 不適切なクライアントの動作リスク

正しく実装されていないクライアントが、誤ったリゾルバを参照してしまったり、リゾルバが見つからなかった場合でもゼロアドレス（null address）を拒否せずに処理を続けてしまう可能性があります。
このような実装ミスにより、意図しない挙動が発生するリスクがあります。

## ワイルドカード対応リゾルバによる誤送金のリスク

ワイルドカード対応のリゾルバでは、あらゆる未登録のサブドメインに対して動的にアドレスを生成して返すことが可能になります。
その結果、ユーザーが名前を打ち間違えた場合でも有効なアドレスが返ってしまい、資金が誤った相手に送金されるリスクが高まります。

こうした誤送金を防ぐために、アプリケーション側で以下のような対策が推奨されます。

* 名前の妥当性チェック（バリデーション）をクライアント側で実装する
* ユーザーが資金を誤送信した場合に、回収可能にする仕組みを導入する

## サブドメインにリゾルバがないことを前提とするアプリケーションとの衝突

一部のアプリケーションでは、特定のサブドメインにリゾルバが「**設定されていないこと**」を前提に設計されている場合があります。
**ERC2544**によって親ドメインがワイルドカード解決を行えるようになると、こうしたアプリケーションの期待に反してレコードが返される可能性があります。

ただし、著者の知る限り、現時点でこのような挙動を期待するアプリケーションは存在せず、実用上の問題は報告されていません。
とはいえ、将来的にこの点が問題になる可能性があるため、注意が必要です。

# 引用

Nick Johnson (@arachnid), 0age (@0age), "ERC-2544: ENS Wildcard Resolution [DRAFT]," Ethereum Improvement Proposals, no. 2544, February 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2544.

# 最後に

今回は「ENSでワイルドカードサブドメインの解決を可能にする仕組みを提案している**ERC2544**」についてまとめてきました！
いかがだったでしょうか？

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