# [ERC6860] HTTPスタイルのWeb3URLを使用してオンチェーンコンテンツにアクセスする仕組みを理解しよう！

> この規格では、RFC3986をweb3://uniswap.eth/のようなEVMメッセージに変換する仕組みを提案しています。

- URL: https://cardene.dev/articles/30bdd75be892cf776b88
- 公開日: 2024-06-27
- 更新日: 2024-06-27
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、HTTPスタイルのWeb3URLをEVM呼び出しメッセージに変換する仕組みを提案している**ERC6860**についてまとめていきます！

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

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

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

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

## 概要

この規格では、[**RFC3986**](https://www.rfc-editor.org/rfc/rfc3986)を`web3://uniswap.eth/`のようなEVMメッセージに変換する仕組みを提案しています。

```js
EVMMessage {
   To: 0xaabbccddee.... // where uniswap.eth's address registered at ENS
   Calldata: 0x
   ...
}
```

:::message
**RFC3986**は、Uniform Resource Identifier (URI)の構文に関する標準を定めたものです。
URIはインターネット上のリソースを識別するために使用されます。

#### 主な内容
URIは5つの部分で構成されます。
- **スキーム (scheme)**
    - リソースへのアクセス方法を指定。
    - 例
        - `http`, `https`, `ftp`
- **認証情報 (authority)**
    - ホスト名やユーザー情報を含む。
    - 例
        - `//user:pass@host:port`
- **パス (path)**
    - リソースの位置を示す。
    - 例
        - `/path/to/resource`
- **クエリ (query)**
    - リソースへの追加情報を提供。
    - 例
        - `?key1=value1&key2=value2`
- **フラグメント (fragment)**
    - リソース内の特定の部分を指す。
    - 例
        - `#section1`

スキームはアルファベットで始まり、アルファベット、数字、プラス(`+`)、ドット(`.`)、ハイフン(`-`)を含むことができます。
また、特殊文字はエンコードする（例: スペースは`%20`にエンコード。）必要があります。
**相対URI**（ベースURIからの相対パスで指定されるURI）や**URIの正規化**（URIを比較するために標準形式に変換するプロセス）などの特徴もあります。

#### 具体例
`https://example.com:8080/path/to/resource?query=123#section2`

- **スキーム**
    - `https`
- **認証情報**
    - `example.com:8080`
- **パス**
    - `/path/to/resource`
- **クエリ**
    - `query=123`
- **フラグメント**
    - `section2`
:::

この規格は、**ERC4804**にに若干の変更を加えた提案になります。

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

[\[ERC4804\] HTTP形式のWeb3 URLからコントラクトを呼び出すことができる仕組みを理解しよう！](/articles/837931ce60ca844aee13)

## 動機

Web3のデータを読み取るとき、通常RPCノードやEtherscan、DappsなどのWeb2プロキシを介してブロックチェーンにアクセスする必要があります。
Web2ユーザーがWeb3コンテンツに直接アクセスすることは難しく、プロキシを介してアクセスすることがほとんどです。
この規格では、Web2ユーザーがプロキシを介さずに直接Web3コンテンツにアクセスできる方法を提供します。
特にオンチェーンにSVGやHTML、メタデータなどのコンテンツが保存されているときこの規格が役に立ちます。
SVGやHTMLなどのURIに互換性のある標準との相互運用性を可能にし、Web3コンテンツを既存のWeb2インフラストラクチャとシームレスに統合することができます。

## 仕様

この規格では、[**RFC2234**](https://www.rfc-editor.org/rfc/rfc2234)というプロトコルやデータ形式の構文を正確に定義するAugmented Backus-Naur Form (ABNF)を使用しています。

:::message
**RFC2234**は、Augmented BNF for Syntax Specifications（ABNF）というデータ形式やプロトコルの構文を正確に記述するために使用される形式について定義しています。

#### 基本情報
ABNFはネットワークプロトコルやデータ形式の構文を記述するための標準的な形式を提案しています。

##### 基本構造
- **規則（Rule）**
    - ABNFは、名前付きの規則（ルール）を使って構文を記述します。
    - 例
        - `rule = definition`
- **リテラル（Literal）**
    - 文字列や文字を直接表現します。
    - 例
        - `"text"`
- **オルタネーション（Alternation）**
    - 複数の選択肢を示します。
    - 例
        - `rule1 / rule2`
- **連接（Concatenation）**
    - シーケンスを示します。
    - 例
        - `rule1 rule2`

##### 記号の使用
- **コロン（=）**
    - 規則の定義を示す。
- **斜線（/）**
    - 選択肢の区切りを示す。
- **括弧（()）**
    - グループ化を示す。
- **角括弧（[]）**
    - オプションを示す。
- **アスタリスク（*）**
    - 繰り返しを示す。
  
##### データ型
- **数字**
    - 数字を使って範囲や回数を指定します。
    - 例
        - `DIGIT = %x30-39`（0から9の数字）
- **文字**
    - 文字の範囲を指定します。
    - 例
        - `ALPHA = %x41-5A / %x61-7A`（AからZおよびaからz）

#### 具体例

```abnf
URI           = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
scheme        = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
hier-part     = "//" authority path-abempty
```

この例では、URIの構文を記述しています。
- **URI**
    - `scheme`、`hier-part`、`query`、`fragment`の組み合わせ。
- **scheme**
    - アルファベットで始まり、アルファベット、数字、`+`、`-`、`.`の組み合わせ。
- **hier-part**
    - `authority`と`path-abempty`の組み合わせ。
:::

Web3 URLは以下の形式を持つASCII文字列です。

```plaintext
web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery
```

- **schema**
    - スキーム部分は`"w3"`または`"web3"`です。
    - この部分はURLがWeb3 URLであることを示します。
  
```plaintext
schema = "w3" / "web3"
```

- **userinfo**
    - `userinfo`はアドレスを表し、EVMコールメッセージの「`from`」フィールドに相当します。
    - 指定されていない場合、プロトコルは送信者アドレスとして`0x0`を使用します。

```plaintext
userinfo = address
```

- **contractName**
    - コントラクト名を指定し、EVMコールメッセージの「`to`」フィールドに相当します。
    - これはアドレスまたはドメイン名です。
    - ドメイン名の場合、それは名前解決サービス（例：Ethereum Name Service）を通じてアドレスに解決されます。

```plaintext
contractName = address / domainName
address = "0x" 20( HEXDIG HEXDIG )
domainName = *( unreserved / pct-encoded / sub-delims )
```

- **chainid**
    - チェーンIDを指定し、どのブロックチェーン上で`contractName`を解決してメッセージを送信するかを決定します。
    - 指定されていない場合、プロトコルは名前サービスプロバイダーの主要チェーン（例：Ethereumの場合は`1`）を使用します。

```plaintext
chainid = %x31-39 *DIGIT
```

- **pathQuery**
    - パスとオプションのクエリからなり、手動モード（manual mode）または自動モード（auto mode）によって構造が異なります。

```plaintext
pathQuery = mPathQuery / aPathQuery
```

#### 相対URL

Web3 URLは相対URLをサポートしており、Resolveモードによってサポートの方法が異なります。

```plaintext
web3UrlRef = web3URL / relativeWeb3URL
relativeWeb3URL = relPathQuery
relPathQuery = relMPathQuery / relAPathQuery
```

#### 具体例

例えば、以下のWeb3 URLを考えます。

```plaintext
web3://0x1234567890abcdef1234567890abcdef12345678@0xabcdef1234567890abcdef1234567890abcdef12:1/path/to/resource?query=param
```

- **schema**
    - `web3`
- **userinfo**
    - `0x1234567890abcdef1234567890abcdef12345678`（EVMコールメッセージの送信者）
- **contractName**
    - `0xabcdef1234567890abcdef1234567890abcdef12`（スマートコントラクトのアドレス）
- **chainid**
    - `1`（Ethereumメインネット）
- **pathQuery**
    - `/path/to/resource?query=param`（リソースのパスとクエリ）

### Resolveモード

Resolveモードには、手動モード（manual mode）と自動モード（auto mode）の2つがあります。

#### Resolveモードの確認方法

プロトコルは、「`to`o」アドレスの`resolveMode`メソッドを呼び出すことでResolveモードを確認します。
このメソッドのSolidity署名は以下の通りです。

```js
function resolveMode() external returns (bytes32);
```

#### サポートされているResolveモード

##### 手動モード（manual mode）

`resolveMode`の戻り値が以下の値と一致する場合に手動モードが使用されます。

```bash
0x6d616e75616c0000000000000000000000000000000000000000000000000000
```

これは、`"manual"`をbytes32形式にエンコードしたものです。

##### 自動モード（auto mode）
`resolveMode`の戻り値が以下のいずれかと一致する場合に自動モードが使用されます。

```plaintext
0x6175746f00000000000000000000000000000000000000000000000000000000
```

これは、`"auto"`をbytes32形式にエンコードしたものです。
または、戻り値が以下の場合も自動モードが使用されます。

```plaintext
0x0000000000000000000000000000000000000000000000000000000000000000
```

さらに、`resolveMode`メソッドの呼び出しがエラーを投げる（メソッドが実装されていないか、メソッド内でエラーが発生する）場合も自動モードが使用されます。

#### その他のモード
上記に該当しない戻り値を返した場合、プロトコルはリクエストを失敗させ、「**unsupported resolve mode**」というエラーを返します。

### 手動モード

手動モードでは、`mPathQuery`として指定された生のデータがメッセージのコールデータとして直接使用されます。

#### 手動モードの構造

##### `mPathQuery`

手動モードの`mPathQuery`は以下のように定義されます。

```plaintext
mPathQuery = mPath [ "?" mQuery ]
```

##### `mPath`
`mPath`は`mPathAbempty`で、スラッシュ`/`で始まるか空である必要があります。

```plaintext
mPath = mPathAbempty ; begins with "/" or is empty
```

`mPathAbempty`は、セグメントの繰り返しとオプションのファイル拡張子からなります。

```plaintext
mPathAbempty = [ *( "/" segment ) "/" segment [ "." fileExtension ] ]
segment = *pchar ; as in RFC 3986
fileExtension = 1*( ALPHA / DIGIT )
```

`segment`は任意の文字列で、`fileExtension`はアルファベットまたは数字の繰り返しです。

##### `mQuery`

`mQuery`は、**RFC3986**に準拠した任意の文字列です。

```plaintext
mQuery = *( pchar / "/" / "?" ) ; as in RFC 3986
```

#### 手動モードの動作

`mPathQuery`が空の場合、送信されるコールデータは`/`（0x2f）になります。
返されたメッセージデータはABIエンコードされたバイト列として扱われ、デコードされたバイト列がフロントエンドに返されます。
フロントエンドに返されるMIMEタイプはデフォルトで`text/html`ですが、ファイル拡張子が存在する場合、そのファイル拡張子からMIMEタイプが推定されます。

#### 相対URLのサポート

手動モードでは、HTTP URLに似た相対URLもサポートされています。

##### `relMPathQuery`
相対URLの構造は以下の通りです。

```plaintext
relMPathQuery = relMPath [ "?" mQuery ]
```

##### `relMPath`
`relMPath`は、絶対パス`mPathAbsolute`、非スキームパス`mPathNoscheme`、空のパス`mPathEmpty`から構成されます。

```plaintext
relMPath = mPathAbsolute ; begins with "/" but not "//"
          / mPathNoscheme ; begins with a non-colon segment
          / mPathEmpty    ; zero characters
```

`mPathAbsolute`はスラッシュで始まり、オプションのファイル拡張子を持つことができます。

```plaintext
mPathAbsolute = "/" [ segmentNz *( "/" segment ) ] [ "." fileExtension ]
```

`mPathNoscheme`はコロン（`:`）を含まないセグメントで始まり、オプションのファイル拡張子を持つことができます。

```plaintext
mPathNoscheme = segmentNzNc *( "/" segment ) [ "." fileExtension ]
segmentNzNc = 1*( unreserved / pct-encoded / sub-delims / "@" )
```

`mPathEmpty`は空の文字列です。

```plaintext
mPathEmpty = 0<pchar>
```

### 自動モード

このセクションは、Web3 URLの自動モード（Auto Mode）について説明しています。自動モードでは、関数の呼び出し方法と引数の指定方法が定義されています。これにより、ユーザーは特定のスマートコントラクトの関数を呼び出し、結果を取得することができます。

#### 自動モードの構造

##### `aPathQuery`
自動モードの`aPathQuery`は、以下のように定義されます。

```plaintext
aPathQuery = aPath [ "?" aQuery ]
```

##### `aPath`
`aPath`は、関数メソッドと引数を含むオプションのパスです。
空またはスラッシュ`/`だけの場合、ターゲットコントラクトは空のコールデータで呼び出されます。
それ以外の場合、標準のSolidityコントラクトABIが使用されます。

```plaintext
aPath = [ "/" [ method *( "/" argument ) ] ]
```

##### `method`
`method`は、呼び出す関数の名前を示す文字列です。

```plaintext
method = ( ALPHA / "$" / "_" ) *( ALPHA / DIGIT / "$" / "_" )
```

##### `argument`
`argument`は、関数メソッドの引数を示します。

```plaintext
argument = boolArg / uintArg / intArg / addressArg / bytesArg / stringArg
```

- **boolArg**
    - 真偽値を示す引数。

```plaintext
boolArg = [ "bool!" ] ( "true" / "false" )
```

- **uintArg**
    - 符号なし整数を示す引数。

```plaintext
uintArg = [ "uint" [ intSizes ] "!" ] 1*DIGIT
```

- **intArg**
    - 符号付き整数を示す引数。

```plaintext
intArg = "int" [ intSizes ] "!" 1*DIGIT
```

- **addressArg**
    - アドレスを示す引数。
  
```plaintext
addressArg = [ "address!" ] ( address / domainName )
```

- **bytesArg**
    - バイト列を示す引数。

```plaintext
bytesArg = [ "bytes!" ] bytes / "bytes1!0x" 1( HEXDIG HEXDIG ) / ... / "bytes32!0x" 32( HEXDIG HEXDIG )
```

- **stringArg**
    - 文字列を示す引数。

```plaintext
stringArg = "string!" *pchar [ "." fileExtension ]
```

##### 自動型検出
型が指定されていない場合、以下の規則で自動的に型が検出されます。

- 数字の場合
    - `uint256`
- 32バイトのデータの場合
    - `bytes32`
- 20バイトのデータの場合
    - `address`
- それ以外のバイトデータの場合
    - `bytes`
- `true`または`false`の場合
    - `bool`
- 上記以外の場合
    - `address`としてドメイン名を解釈し、解決できなければエラーを返します。

##### `aQuery`
クエリ部分は、返されるデータの形式を指定します。

```plaintext
aQuery = attribute *( "&" attribute )
```

- `attribute`
    - `returns`属性は、返されるデータの形式を指定します。

```plaintext
attribute = attrName "=" attrValue
attrName = "returns" / "returnTypes"
attrValue = [ "(" [ retTypes ] ")" ]
retTypes = retType *( "," retType )
retType = retRawType *( "[" [ %x31-39 *DIGIT ] "]" )
retRawType = "(" retTypes ")" / retBaseType
retBaseType = "bool" / "uint" [ intSizes ] / "int" [ intSize ] / "address" / "bytes" [ bytesSizes ] / "string"
```

##### 返されるデータ
`returns`属性が未定義または空の場合、返されるメッセージデータはABIエンコードされたバイト列として扱われます。
`returns`属性が`()`の場合、返されるデータは生のバイト列としてJSON配列にエンコードされます。
その他の場合、`returns`属性のデータ型に従ってABIデコードされ、JSON形式でエンコードされます。

#### 相対URLのサポート
自動モードでは、相対URLは現在のコントラクトに対して相対的に解決され、自己参照、`/`パス、または完全なメソッドと引数を参照できます。

## 例

#### Example 1a
```bash
# 手動モード
web3://w3url.eth/
```

1. ENSを使って`w3url.eth`のアドレスを取得します（チェーンIDは`1`、つまりMainnet）。
2. `resolveMode()`メソッドを呼び出して、`0xDD473FAE`を`calldata`として送信し、戻り値が`manual`であることを確認します。
3. コントラクトに`To`アドレスを指定し、`calldata`として`0x2F`（スラッシュ`/`）を送信します。
4. 返されたデータはABIエンコードされたバイトとして処理され、フロントエンドに返されます。
    - MIMEタイプはデフォルトで`text/html`です。

#### Example 1b
```bash
# 自動モード
web3://w3url.eth/
```

1. ENSを使って`w3url.eth`のアドレスを取得します（チェーンIDは`1`、つまりMainnet）。
2. `resolveMode()`メソッドを呼び出して、`0xDD473FAE`を`calldata`として送信し、戻り値が空であることを確認します（自動モード）。
3. コントラクトに`To`アドレスを指定し、空の`calldata`で呼び出します。
4. 返されたデータはABIエンコードされたバイトとして処理され、フロントエンドに返されます。
    - MIMEタイプは未定義です。

#### Example 2

```
# 自動モード
web3://cyberbrokers-meta.eth/renderBroker/9999
```

1. ENSを使って`cyberbrokers-meta.eth`のアドレスを取得します（チェーンIDは`1`、つまりMainnet）。
2. `resolveMode()`メソッドを呼び出して、`0xDD473FAE`を`calldata`として送信し、戻り値が空であることを確認します（自動モード）。
3. コントラクトに`To`アドレスを指定し、`calldata`として`0x` + `keccak("renderBroker(uint256)")[0:4]` + `abi.encode(uint256(9999))`を送信します。
4. 返されたデータはABIエンコードされたバイトとして処理され、フロントエンドに返されます。
    - MIMEタイプは未定義です。

#### Example 3

```bash
# 手動モード
web3://vitalikblog.eth:5/
```

1. ENSを使って`vitalikblog.eth`のアドレスを取得します（チェーンIDは`5`、つまりGoerli）。
2. `resolveMode()`メソッドを呼び出して、手動モードであることを確認します。
3. コントラクトに`To`アドレスを指定し、`calldata`として`0x2F`（スラッシュ`/`）を送信します。
4. 返されたデータはABIエンコードされたバイトとして処理され、フロントエンドに返されます。
    - MIMEタイプは`text/html`です。

#### Example 4

```bash
# 手動モード
web3://0xe4ba0e245436b737468c206ab5c8f4950597ab7f:42170/
```

1. `resolveMode()`メソッドを呼び出して、手動モードであることを確認します。
2. コントラクトに`To`アドレス（`0xe4ba0e245436b737468c206ab5c8f4950597ab7f`）を指定し、`calldata`として`0x2F`（スラッシュ`/`）を送信します（チェーンIDは`42170`、つまりArbitrum Nova）。
3. 返されたデータはABIエンコードされたバイトとして処理され、フロントエンドに返されます。
    - MIMEタイプは`text/html`です。

#### Example 5

```bash
# 自動モード
web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=(uint256)
```

1. ENSを使って`vitalik.eth`のアドレスを取得します（チェーンIDは`1`、つまりMainnet）。
2. コントラクトの`balanceOf(address)`メソッドを`vitalik.eth`のアドレスで呼び出します。
3. 返されたデータはABIエンコードされた`uint256`型として処理され、JSON形式でフロントエンドに返されます。MIMEタイプは`application/json`です。

#### Example 6

```bash
# 自動モード
web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=()
```

1. ENSを使って`vitalik.eth`のアドレスを取得します（チェーンIDは`1`、つまりMainnet）。
2. コントラクトの`balanceOf(address)`メソッドを`vitalik.eth`のアドレスで呼び出します。
3. 返されたデータは生のバイト列として処理され、JSON形式でフロントエンドに返されます。
    - MIMEタイプは`application/json`です。

## Web3URLの完全なABNF

:::details Web3URLの完全なABNFを開く

```bash
web3URL         = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery
schema          = "w3" / "web3"
userinfo        = address
contractName    = address 
                / domainName
chainid         = %x31-39 *DIGIT

pathQuery       = mPathQuery ; path+query for manual mode
                / aPathQuery ; path+query for auto mode

web3UrlRef      = web3URL 
                / relativeWeb3URL
relativeWeb3URL = relPathQuery
relPathQuery    = relMPathQuery ; Relative URL path+query for manual mode
                / relAPathQuery ; Relative URL path+query for auto mode

mPathQuery      = mPath [ "?" mQuery ]
mPath           = mPathAbempty ; begins with "/" or is empty

relMPathQuery   = relMPath [ "?" mQuery ]
relMPath        = mPathAbsolute ; begins with "/" but not "//"
                / mPathNoscheme ; begins with a non-colon segment
                / mPathEmpty    ; zero characters

mPathAbempty    = [ *( "/" segment ) "/" segment [ "." fileExtension ] ]
mPathAbsolute   = "/" [ segmentNz *( "/" segment ) ] [ "." fileExtension ]
mPathNoscheme   = segmentNzNc *( "/" segment ) [ "." fileExtension ]
mPathEmpty      = 0<pchar>

segment         = *pchar ; as in RFC 3986
segmentNz       = 1*pchar ; as in RFC 3986
segmentNzNc     = 1*( unreserved / pct-encoded / sub-delims / "@" )
                ; as in RFC 3986: non-zero-length segment without any colon ":"

mQuery          = *( pchar / "/" / "?" ) ; as in RFC 3986

aPathQuery      = aPath [ "?" aQuery ]
aPath           = [ "/" [ method *( "/" argument ) ] ]
relAPathQuery   = aPath [ "?" aQuery ]
method          = ( ALPHA / "$" / "_" ) *( ALPHA / DIGIT / "$" / "_" )
argument        = boolArg
                / uintArg
                / intArg
                / addressArg
                / bytesArg
                / stringArg
boolArg         = [ "bool!" ] ( "true" / "false" )
uintArg         = [ "uint" [ intSizes ] "!" ] 1*DIGIT
intArg          = "int" [ intSizes ] "!" 1*DIGIT
intSizes        = "8" / "16" / "24" / "32" / "40" / "48" / "56" / "64" / "72" / "80" / "88" / "96" / "104" / "112" / "120" / "128" / "136" / "144" / "152" / "160" / "168" / "176" / "184" / "192" / "200" / "208" / "216" / "224" / "232" / "240" / "248" / "256"
addressArg      = [ "address!" ] ( address / domainName )
bytesArg        = [ "bytes!" ] bytes
                / "bytes1!0x" 1( HEXDIG HEXDIG )
                / "bytes2!0x" 2( HEXDIG HEXDIG )
                / "bytes3!0x" 3( HEXDIG HEXDIG )
                / "bytes4!0x" 4( HEXDIG HEXDIG )
                / "bytes5!0x" 5( HEXDIG HEXDIG )
                / "bytes6!0x" 6( HEXDIG HEXDIG )
                / "bytes7!0x" 7( HEXDIG HEXDIG )
                / "bytes8!0x" 8( HEXDIG HEXDIG )
                / "bytes9!0x" 9( HEXDIG HEXDIG )
                / "bytes10!0x" 10( HEXDIG HEXDIG )
                / "bytes11!0x" 11( HEXDIG HEXDIG )
                / "bytes12!0x" 12( HEXDIG HEXDIG )
                / "bytes13!0x" 13( HEXDIG HEXDIG )
                / "bytes14!0x" 14( HEXDIG HEXDIG )
                / "bytes15!0x" 15( HEXDIG HEXDIG )
                / "bytes16!0x" 16( HEXDIG HEXDIG )
                / "bytes17!0x" 17( HEXDIG HEXDIG )
                / "bytes18!0x" 18( HEXDIG HEXDIG )
                / "bytes19!0x" 19( HEXDIG HEXDIG )
                / "bytes20!0x" 20( HEXDIG HEXDIG )
                / "bytes21!0x" 21( HEXDIG HEXDIG )
                / "bytes22!0x" 22( HEXDIG HEXDIG )
                / "bytes23!0x" 23( HEXDIG HEXDIG )
                / "bytes24!0x" 24( HEXDIG HEXDIG )
                / "bytes25!0x" 25( HEXDIG HEXDIG )
                / "bytes26!0x" 26( HEXDIG HEXDIG )
                / "bytes27!0x" 27( HEXDIG HEXDIG )
                / "bytes28!0x" 28( HEXDIG HEXDIG )
                / "bytes29!0x" 29( HEXDIG HEXDIG )
                / "bytes30!0x" 30( HEXDIG HEXDIG )
                / "bytes31!0x" 31( HEXDIG HEXDIG )
                / "bytes32!0x" 32( HEXDIG HEXDIG )
stringArg       = "string!" *pchar [ "." fileExtension ]

aQuery          = attribute *( "&" attribute )
attribute       = attrName "=" attrValue
attrName        = "returns"
                / "returnTypes"
attrValue       = [ "(" [ retTypes ] ")" ]
retTypes        = retType *( "," retType )
retType         = retRawType *( "[" [ %x31-39 *DIGIT ] "]" )
retRawType      = "(" retTypes ")"
                / retBaseType
retBaseType      = "bool" / "uint" [ intSizes ] / "int" [ intSize ] / "address" / "bytes" [ bytesSizes ] / "string"
bytesSizes      = %x31-39              ; 1-9
                / ( "1" / "2" ) DIGIT  ; 10-29
                / "31" / "32"          ; 31-32

domainName      = *( unreserved / pct-encoded / sub-delims ) ; As in RFC 3986

fileExtension   = 1*( ALPHA / DIGIT )

address         = "0x" 20( HEXDIG HEXDIG )
bytes           = "0x" *( HEXDIG HEXDIG )

pchar           = unreserved / pct-encoded / sub-delims / ":" / "@" ; As in RFC 3986

pct-encoded     = "%" HEXDIG HEXDIG ; As in RFC 3986

unreserved      = ALPHA / DIGIT / "-" / "." / "_" / "~" ; As in RFC 3986
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
                / "*" / "+" / "," / ";" / "=" ; As in RFC 3986
```

:::


## ERC4804からの変更点

### 修正点 (Corrections)
- **手動モード (Manual mode)**
    - **ERC4804**ではパスとクエリはそのまま使用されていましたが、この規格ではMIMEタイプを決定するために使用されています。
- **自動モード (Auto mode)**
    - **ERC4804**ではクエリに`returns`属性がない場合、返されるデータはABIエンコードされた`bytes32`として扱われていましたが、この規格では実際には返されるデータはABIエンコードされたバイト列として扱われます。

#### 明確化 (Clarifications)
- **正式仕様 (Formal specification)**
    - この規格ではURL形式のABNF定義が追加されています。
- **Resolveモード (Resolve mode)**
    - この規格ではResolveモードがどのように決定されるかの詳細が示されています。
- **手動モード (Manual mode)**
    - URIパーセントエンコーディングの扱い、返されるデータ、MIMEタイプの決定方法についての詳細が示されています。
- **自動モード (Auto mode)**
    - 引数の値のエンコード方法や、`returns`値の形式と取り扱いについての詳細が示されています。

#### 修正点 (Modifications)
- **プロトコル名 (Protocol name)**
    - **ERC4804**では`ethereum-web3://`や`eth-web3://`が定義されていましたが、この規格では削除されています。
- **自動モード (Auto mode)**
    - **対応する型**
        - **ERC4804**では`uint256`、`bytes32`、`address`、`bytes`、`string`のみをサポートしていましたが、この規格ではこれに加えさらに多くの型がサポートされています。
    - **返される整数のエンコード**
    - **ERC4804**では整数を文字列としてエンコードしていましたが、この規格ではEthereum JSON RPC仕様に従い、整数を`0x`で始まる16進数文字列としてエンコードしています。

## 補足

この提案の目的は、Ethereumに対して分散型プレゼンテーションレイヤーを追加することです。
このレイヤーを使用することで、人間が読みやすいURLを使ってオンチェーンでHTML/CSS/JPG/PNG/SVGなどのウェブコンテンツをレンダリングできるようにし、EVM（Ethereum Virtual Machine）を分散型のバックエンドとして活用できます。

### 主な原則

#### 人間が読みやすい
Web3 URLは、Web2 URL（例：`http://`）と同様に人間にとって容易に認識できるものである必要があります。
そのため、アドレスの代わりに名前サービスからの名前を使用することで、読みやすさを向上させます。
また、16進数の`calldata`の代わりに、人間が読みやすいメソッドと引数を使用して`calldata`に変換します。

##### HTTP-URL標準との最大限の互換性
Web3 URLは、HTTP-URL標準と互換性を持つべきで、相対パス、クエリ、フラグメント、パーセントエンコーディングなどが含まれます。
これにより、既存のHTTP-URLのサポート（例：ブラウザによるサポート）を最小限の変更でWeb3 URLに拡張できます。
また、既存のWeb2ユーザーがこの標準についての追加知識を最小限に抑えてWeb3に移行することが容易になります。

##### シンプル
引数に明示的な型を指定する代わりに、`address`、`bytes32`、`uint256`などの引数の型を自動的に検出しています。
これにより、URLの長さを最小限に抑えて混乱を避けることができます。
必要に応じて、明示的な型もサポートされています。

##### 柔軟
コントラクトはエンコーディングルールをオーバーライドできるため、ユーザーが特定したい実際のWebリソースをコントラクトが細かく制御できるようにします。

## 引用

Qi Zhou (@qizhou), Chao Pi (@pichaoqkc), Sam Wilson (@SamWilsn), Nicolas Deschildre (@nand2), "ERC-6860: Web3 URL to EVM Call Message Translation [DRAFT]," Ethereum Improvement Proposals, no. 6860, September 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6860.

## 最後に

今回は「HTTPスタイルのWeb3URLをEVM呼び出しメッセージに変換する仕組みを提案している**ERC6860**」についてまとめてきました！
いかがだったでしょうか？

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