diff --git a/EIPS/eip-6860.md b/EIPS/eip-6860.md new file mode 100644 index 00000000000000..a3d8f00c7e1266 --- /dev/null +++ b/EIPS/eip-6860.md @@ -0,0 +1,387 @@ +--- +eip: 6860 +title: Web3 URL to EVM Call Message Translation +description: A translation of an HTTP-style Web3 URL to an EVM call message +author: Qi Zhou (@qizhou), Chao Pi (@pichaoqkc), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-4804-web3-url-to-evm-call-message-translation/8300 +status: Draft +type: Standards Track +category: ERC +created: 2023-09-29 +requires: 137 +--- + +## Abstract + +This standard translates an RFC 2396 URI like `web3://uniswap.eth/` to an EVM message such as: + +``` +EVMMessage { + To: 0xaabbccddee.... // where uniswap.eth's address registered at ENS + Calldata: 0x + ... +} +``` + +⚠️ This proposal updates [ERC-4804](./eip-4804.md) with minor corrections, clarifications and modifications. + +## Motivation + +Currently, reading data from Web3 generally relies on a translation done by a Web2 proxy to Web3 blockchain. The translation is mostly done by the proxies such as dApp websites/node service provider/etherscan, which are out of the control of users. The standard here aims to provide a simple way for Web2 users to directly access the content of Web3, especially on-chain Web contents such as SVG/HTML. Moreover, this standard enables interoperability with other standards already compatible with URIs, like SVG/HTML. + +## Specification + +This specification only defines read-only (i.e. Solidity's `view` functions) semantics. State modifying functions may be defined as a future extension. + +This specification uses the Augmented Backus-Naur Form (ABNF) notation of RFC 2234. The complete URI syntax is listed in Appendix A. + +A Web3 URL is an ASCII string in the following form : + +``` +web3URL = web3Schema [ userinfo "@" ] contractName [ ":" chainid ] pathQuery +web3Schema = "w3://" / "web3://" +userinfo = address +``` + +**userinfo** indicates which user is calling the EVM, i.e., "From" field in EVM call message. If not specified, the protocol will use 0x0 as the sender address. + +``` +contractName = address + / domainName +address = "0x" 20( HEXDIG HEXDIG ) +domainName = domainPart *( "." domainPart ) "." nsSuffix +``` + +**contractName** indicates the contract to be called, i.e., "To" field in the EVM call message. If the **contractName** is an address then "To" will be the address. Otherwise, the name is from a name service. In the second case, **nsSuffix** will be the suffix from name service providers such as "eth", etc. The way to translate the name from a name service to an address will be discussed in later EIPs. + +``` +chainid = 1*DIGIT +``` + +**chainid** indicates which chain to resolve **contractName** and call the message. If not specified, the protocol will use the same chain as the name service provider, e.g., 1 for eth. If no name service provider is available, the default chainid is 1. + +``` +pathQuery = pathQueryManual + \ pathQueryAuto +``` + +**pathQuery**, made of the path and optional query, will have a different structure whether the resolve mode is "manual" or "auto". + + +### Resolve Mode + +Once the "To" address and chainid are determined, the protocol will check the resolver mode of contract by calling the `resolveMode` method of the "To" address. The Solidity signature of `resolveMode` is: + +```solidity +function resolveMode() external returns (bytes32); +``` + +The protocol currently supports two resolve modes: auto and manual. + +- The manual mode will be used if the `resolveMode` return value is `0x6d616e75616c0000000000000000000000000000000000000000000000000000`, i.e., "manual" in bytes32 +- The auto mode will be used if : + - the `resolveMode` return value is `0x6175746f00000000000000000000000000000000000000000000000000000000`, i.e, "auto" in bytes32, or + - the `resolveMode` return value is `0x0000000000000000000000000000000000000000000000000000000000000000`, or + - the call to `resolveMode` throws an error (method not implemented or error thrown from the method) +- Otherwise, the protocol will fail the request with the error "unsupported resolve mode". + +#### Manual Mode + +``` +pathQueryManual = pathManual [ "?" queryManual ] + +pathManual = [ *( "/" segment ) "/" segment [ "." fileExtension ] ] +segment = *pchar ; as in RFC 3986 +fileExtension = 1*( ALPHA / DIGIT ) + +queryManual = *( pchar / "/" / "?" ) ; as in RFC 3986 +``` + +The manual mode will use the raw **pathQueryManual** as calldata of the message directly (no percent-encoding decoding will be done). If **pathQueryManual** is empty, the sent calldata will be ``/`` (0x2f). + +The returned message data will be treated as ABI-encoded bytes and the decoded bytes will be returned to the frontend. + +The MIME type returned to the frontend is ``text/html`` by default, but will be overriden if a **fileExtension** is present. In this case, the MIME type will be deduced from the filename extension. + +#### Auto Mode + +``` +pathQueryAuto = pathAuto [ "?" queryAuto ] +pathAuto = [ "/" [ method *( "/" argument ) ] ] +``` + +In the auto mode, if **pathAuto** is empty or "/", then the protocol will call the target contract with empty calldata. Otherwise, the calldata of the EVM message will use standard Solidity contract ABI. + +``` +method = ( ALPHA / "$" / "_" ) *( ALPHA / DIGIT / "$" / "_" ) +``` + +**method** is a string of the function method to be called + +``` +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 ) + ... + \ "bytes32!0x" 32( HEXDIG HEXDIG ) +stringArg = "string!" *pchar [ "." fileExtension ] +``` + +**argument** is an argument of the method with a type-agnostic syntax of ``[ type "!" ] value``. If **type** is specified, the value will be translated to the corresponding type. The protocol currently supports these basic types: bool, int, uint, int<X>, uint<X> (with X ranging from 8 to 256 in steps of 8), address, bytes<X> (with X ranging from 1 to 32), bytes, and string. If **type** is not specified, then the type will be automatically detected using the following rule in a sequential way: + + 1. **type**="uint256", if **value** is numeric; or + 2. **type**="bytes32", if **value** is in the form of 0x+32-byte-data hex; or + 3. **type**="address", if **value** is in the form of 0x+20-byte-data hex; or + 4. **type**="bytes", if **value** is in the form of 0x followed by any number of bytes besides 20 or 32; or + 5. else **type**="address" and parse the argument as a domain name in the form of `domainPart *( "." domainPart ) "." nsSuffix`. In this case, the actual value of the argument will be obtained from **nsSuffix**, e.g., eth. If **nsSuffix** is not supported, an unsupported NS provider error will be returned. + +``` +queryAuto = attribute *( "&" attribute ) +attribute = attrName "=" attrValue +attrName = "returns" + / "returnTypes" +attrValue = [ "(" [ retTypes ] ")" ] +retTypes = retType *( "," retType ) +retType = *( "[]" ) retRawType +retRawType = "bool" / "uint" [ intSizes ] / "int" [ intSize ] / "address" / "bytes" [ bytesSizes ] / "string" +bytesSizes = DIGIT + / ( "1" / "2" ) DIGIT + / "31" / "32" +``` + +The "returns" attribute in **queryAuto** tells the format of the returned data. + +- If the "returns" attribute value is undefined or empty, the returned message data will be treated as ABI-encoded bytes and the decoded bytes will be returned to the frontend. The MIME type returned to the frontend will be undefined by default, but will be overriden if the last argument is of string type and has a **fileExtension**, in which case the MIME type will be deduced from the filename extension. +- If the "returns" attribute value is equal to "()", the raw bytes of the returned message data will be returned, encoded as a "0x"-prefixed hex string in an array in JSON format: ``["0xXXXXX"]`` +- Otherwise, the returned message data will be ABI-decoded in the data types specified in the **returns** value and encoded in JSON format. The encoding of the data will follow the Ethereum JSON-RPC format: + - Unformatted data (bytes, address) will be encoded as hex, prefixed with "0x", two hex digits per byte + - Quantities (integers) will be encoded as hex, prefix with "0x", the most compact representation (slight exception: zero should be represented as "0x0") + - Boolean and strings will be native JSON boolean and strings + +If multiple "returns" attributes are present, the value of the last "returns" attribute will be applied. Note that "returnTypes" is the alias of "returns", but it is not recommended to use and is mainly for [ERC-4804](./eip-4804.md) backward-compatible purpose. + +### Examples + +#### Example 1a + +``` +web3://w3url.eth/ +``` + +where the contract of **w3url.eth** is in manual mode. + +The protocol will find the address of **w3url.eth** from ENS in chainid 1 (Mainnet). Then the protocol will call the address with "Calldata" = `keccak("resolveMode()")[0:4]` = "0xDD473FAE", which returns "manual" in ABI-type "(bytes32)". After determining the manual mode of the contract, the protocol will call the address with "To" = **contractAddress** and "Calldata" = "0x2F". The returned data will be treated as ABI-type "(bytes)", and the decoded bytes will be returned to the frontend, with the information that the MIME type is ``text/html``. + +#### Example 1b + +``` +web3://w3url.eth/ +``` + +where the contract of **w3url.eth** is in auto mode. + +The protocol will find the address of **w3url.eth** from ENS in chainid 1 (Mainnet). Then the protocol will call the address with "Calldata" = `keccak("resolveMode()")[0:4]` = "0xDD473FAE", which returns "", i.e., the contract is in auto mode. After determining the auto mode of the contract, the protocol will call the address with "To" = **contractAddress** and "Calldata" = "". The returned data will be treated as ABI-type "(bytes)", and the decoded bytes will be returned to the frontend, with the information that the MIME type is undefined. + +#### Example 2 + +``` +web3://cyberbrokers-meta.eth/renderBroker/9999 +``` + +where the contract of **cyberbrokers-meta.eth** is in auto mode. + +The protocol will find the address of **cyberbrokers-meta.eth** from ENS on chainid 1 (Mainnet). Then the protocol will call the address with "Calldata" = `keccak("resolveMode()")[0:4]` = "0xDD473FAE", which returns "", i.e., the contract is in auto mode. After determining the auto mode of the contract, the protocol will call the address with "To" = **contractAddress** and "Calldata" = "0x" + `keccak("renderBroker(uint256)")[0:4] + abi.encode(uint256(9999))`. The returned data will be treated as ABI-type "(bytes)", and the decoded bytes will be returned to the frontend, with the information that the MIME type is undefined. + +#### Example 3 + +``` +web3://vitalikblog.eth:5/ +``` + +where the contract of **vitalikblog.eth:5** is in manual mode. + +The protocol will find the address of **vitalikblog.eth** from ENS on chainid 5 (Goerli). Then after determing the contract is in manual mode, the protocol will call the address with "To" = **contractAddress** and "Calldata" = "0x2F" with chainid = 5. The returned data will be treated as ABI-type "(bytes)", and the decoded bytes will be returned to the frontend, with the information that the MIME type is ``text/html``. + +#### Example 4 + +``` +web3://0xe4ba0e245436b737468c206ab5c8f4950597ab7f:42170/ +``` + +where the contract "0xe4ba0e245436b737468c206ab5c8f4950597ab7f:42170" is in manual mode. + +After determing the contract is in manual mode, the protocol will call the address with "To" = "0xe4ba0e245436b737468c206ab5c8f4950597ab7f" and "Calldata" = "0x2F" with chainid = 42170 (Arbitrum Nova). The returned data will be treated as ABI-type "(bytes)", and the decoded bytes will be returned to the frontend, with the information that the MIME type is ``text/html``. + +#### Example 5 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=(uint256) +``` + +where the contract "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" is in auto mode. + +The protocol will find the addresses of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the contract with the **vitalik.eth**'s address. The returned data from the call of the contract will be treated as ABI-type "(uint256)", and the decoded data will be returned to the frontend in JSON format like `[ "0x9184e72a000" ]`, with the information that the MIME type is ``application/json``. + +#### Example 6 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=() +``` + +where the contract ”0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48“ is in auto mode. + +The protocol will find the address of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the address. The returned data from the call of the contract will be treated as raw bytes and will be encoded in JSON format like `["0x000000000000000000000000000000000000000000000000000009184e72a000"]` and returned to the frontend, with the information that the MIME type is ``application/json``. + +### Appendix A: Complete ABNF for Web3 URLs + +``` +web3URL = web3Schema [ userinfo "@" ] contractName [ ":" chainid ] pathQuery +web3Schema = "w3://" / "web3://" +userinfo = address +contractName = address + / domainName +chainid = 1*DIGIT + +pathQuery = pathQueryManual + \ pathQueryAuto + +pathQueryManual = pathManual [ "?" queryManual ] +pathManual = [ *( "/" segment ) "/" segment [ "." fileExtension ] ] +segment = *pchar ; as in RFC 3986 + +queryManual = *( pchar / "/" / "?" ) ; as in RFC 3986 + +pathQueryAuto = pathAuto [ "?" queryAuto ] +pathAuto = [ "/" [ method *( "/" argument ) ] ] +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 ] + +queryAuto = attribute *( "&" attribute ) +attribute = attrName "=" attrValue +attrName = "returns" + / "returnTypes" +attrValue = [ "(" [ retTypes ] ")" ] +retTypes = retType *( "," retType ) +retType = *( "[]" ) retRawType +retRawType = "bool" / "uint" [ intSizes ] / "int" [ intSize ] / "address" / "bytes" [ bytesSizes ] / "string" +bytesSizes = DIGIT + / ( "1" / "2" ) DIGIT + / "31" / "32" + +domainName = domainPart *( "." domainPart ) "." nsSuffix +domainPart = 1*( ALPHA / DIGIT / "-" ) +nsSuffix = 1*( ALPHA / DIGIT / "-" ) + +fileExtension = 1*( ALPHA / DIGIT ) + +address = "0x" 20( HEXDIG HEXDIG ) +bytes = "0x" *( HEXDIG HEXDIG ) + +pchar = unreserved / pct-encoded / sub-delims / ":" / "@" ; As in RFC 3986 +qchar = unreserved / pct-encoded / other-delims / ":" / "@" + +pct-encoded = "%" HEXDIG HEXDIG ; As in RFC 3986 + +unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" ; As in RFC 3986 +sub-delims = query-delims / other-delims ; As in RFC 3986 +query-delims = "=" / "&" +other-delims = "!" / "$" / "'" / "(" / ")" + / "*" / "+" / "," / ";" + +``` + +### Appendix B: Changes versus [ERC-4804](./eip-4804.md) + +#### Corrections + +- Manual mode : [ERC-4804](./eip-4804.md) stipulates that there is no interpretation of the path [ "?" query ]. This ERC indicates that there is in fact an interpretation of the path, for MIME type determination purpose. +- Auto mode : If there is no **returns** attribute in **query**, [ERC-4804](./eip-4804.md) stipulates that the returned data is treated as ABI-encoded bytes32. This ERC indicates that in fact the returned data is treated as ABI-encoded bytes. + +#### Clarifications + +- Formal specification: This ERC add a ABNF definition of the URL format. +- Resolve mode: This ERC indicates more details on how the resolve mode is determined. +- Manual mode : This ERC indicates how to deal with URI-percent-encoding, the return data, and how the MIME type is determined. +- Auto mode : This ERC indicates in more details the encoding of the argument values, as well as the format and handling of the **returns** value. +- Examples : This ERC add more details to the examples. + +#### Modifications + +- Protocol name: [ERC-4804](./eip-4804.md) mentionned ``ethereum-web3://`` and ``eth-web3://``, these are removed. +- Auto mode: Supported types: [ERC-4804](./eip-4804.md) supported only uint256, bytes32, address, bytes, and string. This ERC add more types. +- Auto mode: Encoding of returned integers when a **returns** attribute is specified: [ERC-4804](./eip-4804.md) suggested in example 5 to encode integers as strings. This ERC indicates to follow the Ethereum JSON RPC spec and encode integers as a hex string, prefixed with "0x". + +## Rationale + +The purpose of the proposal is to add a decentralized presentation layer for Ethereum. With the layer, we are able to render any web content (including HTML/CSS/JPG/PNG/SVG, etc) on-chain using human-readable URLs, and thus EVM can be served as a decentralized backend. The design of the standard is based on the following principles: + +- **Human-readable**. The Web3 URL should be easily recognized by human similar to Web2 URL (`http://`). As a result, we support names from name services to replace address for better readability. In addition, instead of using calldata in hex, we use human-readable method + arguments and translate them to calldata for better readability. + +- **Maximum-Compatible with HTTP-URL standard**. The Web3 URL should be compatible with HTTP-URL standard including relative pathing, query, fragment, percent-encoding, etc so that the support of existing HTTP-URL (e.g., by browser) can be easily extended to Web3 URL with minimal modification. This also means that existing Web2 users can easily migrate to Web3 with minimal extra knowledge of this standard. + +- **Simple**. Instead of providing explicit types in arguments, we use a "maximum likelihood" principle of auto-detecting the types of the arguments such as address, bytes32, and uint256. This could greatly minimize the length of URL, while avoiding confusion. In addition, explicit types are also supported to clear the confusion if necessary. + +- **Flexible**. The contract is able to override the encoding rule so that the contract has fine-control of understanding the actual Web resources that the users want to locate. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md).