Skip to content
139 changes: 104 additions & 35 deletions docs/addresses/interoperable-address-explainer.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,123 @@
Introducing: Interoperable Addresses
====

*Specs in the formal format are written [here](/specs/addresses/cross-chain-interoperable-addresses-spec.md)*.
*Specs in the formal, normative format can be found [here](/specs/addresses/cross-chain-interoperable-addresses-spec.md)*.

Interoperable Addresses is a standard defining address formats for a multi-chain world.

For a normal user, they look like this:
They contain the following information:

<p>
<code>
<span style="color:grey">3::</span><span style="color: blue">vitalik.eth</span>@<span style="color: magenta">eth</span>#<span style="color:grey">5966be0f</span>
</code>
</p>
```solidity
struct InteroperableAddress {
bytes targetChainId;
bytes targetAddress;
bytes resolverChainId;
bytes resolverAddress;
}
```

- <code><span style="color:grey">3::</span></code>: the Interoperable Address Resolver version, which your wallet may very well omit.
- <code><span style="color: blue">vitalik.eth</span></code>: A human-readable name, specific to a chain. Supported by (anything looking like) ENS.
- <code><span style="color: magenta">eth</span></code>: A human-readable chain name, defined by either https://github.com/ethereum-lists/chains if available (same registry for ERC-3770) or CAIP-2 in all other cases.
- <code><span style="color:grey">5966be0f</span></code>: A checksum so you can at a glance validate what you see matches what you expect to be under the hood.
Let's take an example:

---
<pre>
InteroperableAddress addy = InteroperableAddress({
targetChainId: hex'<span style="color:green">0000</span><span style="color:orange">01</span>',
targetAddress: hex'<span style="color:blue">D8DA6BF26964AF9D7EED9E03E53415D37AA96045</span>',
resolverChainId: hex'<span style="color:black">0000<span style="color:pink">01</span>',
resolverAddress: hex'<span style="color:magenta">00000000000C2E074EC69A0DFB2997BA6C7D2E1E</span>'
})
</pre>

For nerds and computers, however, they look like this:
Would be seen by users like this:

<p>
<code>
<span style="color:grey">3:</span><span style="color:green">eip155:1</span>:<span style="color: red">0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e</span>::<span style="color:magenta">eip155:1</span>:<span style="color: blue">0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045</span>#<span style="color:grey">5966be0f</span>
</code>
</p>
<pre>
<span style="color:blue">vitalik.eth</span>@<span style="color:green">eip:155</span>:<span style="color:orange">1</span>#<span style="color:grey">618AD0D1</span>
</pre>

- <code><span style="color:grey">3:</span></code>: Interoperable Address version.
- <code><span style="color:green">eip155:1</span></code>: CAIP-2 id of chain where the naming registry is located
- <code><span style="color: red">0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e</span></code>: Address where to find the naming registry
- <code><span style="color:magenta">eip155:1</span></code>: CAIP-2 id of chain we are referring to
- <code><span style="color: blue">0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045</span></code>: Chain-specific address we are targeting in that chain
- <code><span style="color:grey">5966be0f</span></code>: Checksum of all previous fields, to make sure nothing got lost in transit
...and in memory, it would actually be laid out like this:

---
<pre>
C000618AD0D10000000000000000000000000000000000000000000000000001
<span style="color:green">0000</span><span style="color:orange">01</span><span style="color: grey">000000000000000000000000000000000000000000000000000000000C</span>
<span style="color:blue">D8DA6BF26964AF9D7EED9E03E53415D37AA96045</span><span style="color: grey">000000000000000000000028</span>
<span style="color:black">0000</span><span style="color:pink">01</span><span style="color: grey">000000000000000000000000000000000000000000000000000000000C</span>
<span style="color:magenta">00000000000C2E074EC69A0DFB2997BA6C7D2E1E</span><span style="color: grey">000000000000000000000028</span>
</pre>

The Interoperable Address is a format for strings to:
- Fully specify an address on a particular chain (using CAIP-10), supporting any EVM-ecosystem chain and any chain on SLIP-044.
- Also include the information to display it to a human in a readable way, as displayed above.
It includes:
- A full specification of what (address,chain) pair we're referring to
- A full specification of on which (address,chain) pair the contract responsible for resolving addresses to names lives
- ... and a full 32-byte word before any of that?

Some of its features are:
- Uses mature technologies such as CAIP-10 and ENS.
- Is extensible for future resolving mechanisms (eg: ERC-7785, when/if it reaches production).
- Resolution of Interoperable Addresses to human readable names is fully deterministic.
Said word is what enables us to make this standard extensible, let's take a closer look:

<pre>
MSB LSB
<span style="color: red">C000</span><span style="color:green">6164DA1</span><span style="color:blue">00000000000000000000000000000000000000000000000000001</span>
^^^^-------------------------------------------------------------- 255-240 <span style="color:red">Interoperable Address Version</span>
^^^^^^^^ ----------------------------------------------------- 239-208 <span style="color:green">Checksum</span>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 207-0 <span style="color:blue">Resolver Interface Key</span>
</pre>

- <span style="color:red">Interoperable Address Version</span>: First two bytes of the payload allows us to discern how we should interpret it, more on this later
- <span style="color:green">Checksum</span>: Covers only the (address,chain) we are referring to -> is independent of the method used to display the address, or how it is serialized -> makes naming collisions evident
- <span style="color:blue">Resolver Interface Key</span>: in addresses version `0xC000`, instructs us on what to expect from `nameContractAddress` and `nameContractChain`. In particular, this value means to expect an ENSIP-11 contract.

But wait! I actually want...
- A canonical representation: use Interoperable Address Version `0x8000`
- To be as space-efficient as possible for EVM chains: use Interoperable Address Version `0x0000` or `0x0001`
- To display human-readable chain names to users, but not rely on centralized lists: define a new 'Resolver Interface Key' as soon as ERC-7785 reaches production

What some other resolvers look like:

`0x0000`:
<pre>
MSB LSB
0x<span style="color: red">0000</span><span style="color: green">618ad0d1</span><span style="color: blue">00000000000</span><span style="color: magenta">1D8DA6BF26964AF9D7EED9E03E53415D37AA96045</span>
^^^^------------------------------------------------------------- 255-240 <span style="color: red">Interoperable Address version</span>
^^^^^^^^----------------------------------------------------- 239-208 <span style="color: green">Checksum</span>
^^^^^^^^^^^^----------------------------------------- 207-160 <span style="color: blue">Chainid</span>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- 159-0 <span style="color: magenta">Address</<span>
</pre>
Is represented as:
<pre>
<span style="color: magenta">0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045</span>@eip155:<span style="color: blue">1</span>#<span style="color: green">618AD0D1</span>
</pre>

To fit in a single word, it sacrifices:
* Ability to refer to CAIP-2 namespaces other than eip155 (it's implied by the version number)
* Ability to represent addresses longer than 20 bytes
* Ability to refer to chains with chainids longer than 48 bits (rules out ERC-7785)

`0x8000`:
```
MSB LSB
8000618AD0D10000000000000000000000000000000000000000000000000000
^^^^-------------------------------------------------------------- 255-240 Interoperable Address Version
^^^^^^^^ ----------------------------------------------------- 239-208 Checksum
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 207-0 not used, always zero
MSB LSB
00000100000000000000000000000000000000000000000000000000000000C0
^^^^^^------------------------------------------------------------ 255-208 bytes array payload
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- 9-207 padding
^^ - 0-8 2*6 (payload length)
MSB LSB
D8DA6BF26964AF9D7EED9E03E53415D37AA96045000000000000000000000028
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------------------ 255-97 bytes array payload
^^^^^^^^^^^^^^^^^^^^^^ --- 9-96 padding
^^ - 0-8 2*20 (payload length)
```
Is represented as:
`0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045@eip155:1#618AD0D1`

Some properties of this system:
- Interoperable addresses of any version can always be converted to format `0x8000` -> forwards-compatibility and graceful degradation of interfaces.
- Interoperable addresses of any version can always be casted to a valid CAIP-10.
- Addresses of any length in chains of any supported CAIP-2 namespace can be represented
- Future-proofing for developments on future resolving mechanisms.
- Implementable right now with current tools & standards.
- Edge cases are securely abstractable to a library/SDK for wallets.
- Does not enshrine a particular ENS contract, making it flexible for a future naming-only rollup, instances on other rollups or even an ENS fork.
- Does not enshrine a particular naming contract, making it flexible for a future naming-only rollup, instances on other rollups or even an ENS competitor.

And, in all honesty, it has some drawbacks as well:
- Exposes a version number to users (although that can be mitigated by wallet UX)
And, in all honesty, some drawbacks as well:
- Supporting different name resolution contracts mean human-readable name -> machine address resolution can produce different results (which is mitigated by the checksum)
- Wallets will have to maintain a set of name resolving contracts considered trustworthy, with a trust model similar to RPC urls (can be taken care of by a library/SDK, but user overrides should be supported).
Loading