Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ERC-6860: Update ERC-6860 #84

Merged
merged 11 commits into from
Nov 2, 2023
223 changes: 138 additions & 85 deletions ERCS/erc-6860.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ requires: 137

## Abstract

This standard translates an RFC 2396 URI like `web3://uniswap.eth/` to an EVM message such as:
This standard translates an [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) URI like `web3://uniswap.eth/` to an EVM message such as:

```
EVMMessage {
Expand All @@ -33,13 +33,13 @@ Currently, reading data from Web3 generally relies on a translation done by a We

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.
This specification uses the Augmented Backus-Naur Form (ABNF) notation of [RFC 2234](https://www.rfc-editor.org/rfc/rfc2234). 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://"
web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery
schema = "w3" / "web3"
userinfo = address
```

Expand All @@ -49,24 +49,36 @@ userinfo = address
contractName = address
/ domainName
address = "0x" 20( HEXDIG HEXDIG )
domainName = domainPart *( "." domainPart ) "." nsSuffix
domainName = *( unreserved / pct-encoded / sub-delims ) ; As in RFC 3986
```

**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.
**contractName** indicates the contract to be called, i.e., the "To" field in the EVM call message. If the **contractName** is an address then it will be used for the "To" field. Otherwise, **contractName** is a domain name from a domain name service, and it must be resolved to an address to use for the "To" field.

The way to resolve the domain name from a domain name service to an address is specified in [ERC-6821](./eip-6821.md) for the Ethereum Name service, and will be discussed in later ERCs for other name services.

```
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.
**chainid** indicates which chain to resolve **contractName** and call the message. If not specified, the protocol will use the primary chain of the name service provider used, e.g., 1 for eth. If no name service provider was used, the default chainid is 1.

```
pathQuery = pathQueryManual
\ pathQueryAuto
pathQuery = mPathQuery ; path+query for manual mode
/ aPathQuery ; path+query for auto mode
```

**pathQuery**, made of the path and optional query, will have a different structure whether the resolve mode is "manual" or "auto".

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

Relative URLs are supported, but the support differs based on the resolve mode.


### Resolve Mode

Expand All @@ -88,29 +100,47 @@ The protocol currently supports two resolve modes: auto and manual.
#### Manual Mode

```
pathQueryManual = pathManual [ "?" queryManual ]
mPathQuery = mPath [ "?" mQuery ]

pathManual = [ *( "/" segment ) "/" segment [ "." fileExtension ] ]
mPath = mPathAbempty ; begins with "/" or is empty
mPathAbempty = [ *( "/" segment ) "/" segment [ "." fileExtension ] ]
segment = *pchar ; as in RFC 3986
fileExtension = 1*( ALPHA / DIGIT )

queryManual = *( pchar / "/" / "?" ) ; as in RFC 3986
mQuery = *( 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 manual mode will use the raw **mPathQuery** as calldata of the message directly (no percent-encoding decoding will be done). If **mPathQuery** 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.

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

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

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 ":"
```

Support for manual mode relative URLs is similar to HTTP URLs : URLs relative to the current contract are allowed, both with an absolute path and a relative path.

#### Auto Mode

```
pathQueryAuto = pathAuto [ "?" queryAuto ]
pathAuto = [ "/" [ method *( "/" argument ) ] ]
aPathQuery = aPath [ "?" aQuery ]
aPath = [ "/" [ 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.
In the auto mode, if **aPath** 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 / "$" / "_" )
Expand All @@ -120,34 +150,35 @@ method = ( ALPHA / "$" / "_" ) *( ALPHA / DIGIT / "$" / "_" )

```
argument = boolArg
\ uintArg
\ intArg
\ addressArg
\ bytesArg
\ stringArg
/ 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 )
/ "bytes1!0x" 1( HEXDIG HEXDIG )
/ "bytes2!0x" 2( HEXDIG HEXDIG )
...
\ "bytes32!0x" 32( 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&lt;X&gt;, uint&lt;X&gt; (with X ranging from 8 to 256 in steps of 8), address, bytes&lt;X&gt; (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
1. **type**="uint256", if **value** is digits; 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.
5. else **type**="address" and parse the argument as a domain name. If unable to resolve the domain name, an unsupported name service provider error will be returned.


```
queryAuto = attribute *( "&" attribute )
aQuery = attribute *( "&" attribute )
attribute = attrName "=" attrValue
attrName = "returns"
/ "returnTypes"
Expand All @@ -160,9 +191,9 @@ bytesSizes = DIGIT
/ "31" / "32"
```

The "returns" attribute in **queryAuto** tells the format of the returned data.
The "returns" attribute in **aQuery** 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 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. (Note that **fileExtension** is not excluded from the string argument given to the smartcontract)
- 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
Expand All @@ -171,6 +202,12 @@ The "returns" attribute in **queryAuto** tells the format of the returned data.

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.

```
relAPathQuery = aPath [ "?" aQuery ]
```

Support for auto mode relative URLs is limited : URLs relative to the current contract are allowed and will either reference itself (empty), the ``/`` path or a full method and its arguments.

### Examples

#### Example 1a
Expand Down Expand Up @@ -246,102 +283,118 @@ The protocol will find the address of **vitalik.eth** from ENS on chainid 1 (Mai
### Appendix A: Complete ABNF for Web3 URLs

```
web3URL = web3Schema [ userinfo "@" ] contractName [ ":" chainid ] pathQuery
web3Schema = "w3://" / "web3://"
web3URL = schema "://" [ userinfo "@" ] contractName [ ":" chainid ] pathQuery
schema = "w3" / "web3"
userinfo = address
contractName = address
/ domainName
chainid = 1*DIGIT

pathQuery = pathQueryManual
\ pathQueryAuto
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>

pathQueryManual = pathManual [ "?" queryManual ]
pathManual = [ *( "/" segment ) "/" segment [ "." fileExtension ] ]
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 ":"

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

pathQueryAuto = pathAuto [ "?" queryAuto ]
pathAuto = [ "/" [ method *( "/" argument ) ] ]
aPathQuery = aPath [ "?" aQuery ]
aPath = [ "/" [ method *( "/" argument ) ] ]
relAPathQuery = aPath [ "?" aQuery ]
method = ( ALPHA / "$" / "_" ) *( ALPHA / DIGIT / "$" / "_" )
argument = boolArg
\ uintArg
\ intArg
\ addressArg
\ bytesArg
\ stringArg
/ 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 )
/ "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 )
aQuery = attribute *( "&" attribute )
attribute = attrName "=" attrValue
attrName = "returns"
/ "returnTypes"
attrValue = [ "(" [ retTypes ] ")" ]
retTypes = retType *( "," retType )
retType = *( "[]" ) retRawType
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 / "-" )
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
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 = "!" / "$" / "'" / "(" / ")"
/ "*" / "+" / "," / ";"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "=" ; As in RFC 3986

```

Expand Down
Loading