Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions docs/evm/nac-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,69 @@ bytes memory michelineResult = gateway.callMichelsonView(

A complete worked example (using the low-level `staticcall` pattern) is available in [`crac_michelson_view_staticcall.sol`](https://gitlab.com/tezos/tezos/-/blob/master/etherlink/kernel_latest/solidity_examples/crac_michelson_view_staticcall.sol).

## Address translation

Cross-interface calls run under the caller's alias (see [Accounts and Aliases](/overview/accounts-and-aliases)). The gateway exposes two `view` functions to translate between native addresses and aliases. Both take the address as a `string` in its printable form (`0x…` hex for the EVM interface, base58check for the Michelson interface) rather than as a Solidity `address`, and identify interfaces by a runtime id: `0` for the Michelson interface, `1` for the EVM interface.

```solidity
interface INativeAtomicGateway {
function originOf(
string calldata addr,
uint8 sourceRuntime
) external view returns (uint8 kind, uint8 homeRuntime, string memory nativeAddress);

function resolveAddress(
string calldata addr,
uint8 sourceRuntime,
uint8 targetRuntime
) external view returns (bool classified, uint8 res, string memory translated);

error InvalidRuntimeId(uint8 received);
}
```

### `originOf`

`originOf(addr, sourceRuntime)` returns how the address `addr` of the interface `sourceRuntime` is classified:

| `kind` | Meaning | `homeRuntime` | `nativeAddress` |
|---|---|---|---|
| `0` (Unknown) | The address is malformed or has not been seen by the kernel yet | `0` | `""` |
| `1` (Native) | An account native to `sourceRuntime` | `sourceRuntime` | `addr` |
| `2` (Alias) | The alias of an account native to the other interface | The interface the account is native to | The native address of that account |

The kernel records the origin of an account when it is first used: Michelson user accounts (`tz1…`, `tz2…`, `tz3…`) are always Native, Michelson smart contracts are recorded at origination, EVM accounts when they first sign a transaction or when they have code, and aliases when a cross-interface call creates them. In particular, an EVM address that has only received funds is Unknown.

For example, to check whether the caller is a Michelson account:

```solidity
(uint8 kind, , string memory native) = gateway.originOf(
Strings.toHexString(msg.sender), // OpenZeppelin helper: lowercase "0x…" string
1
);
if (kind == 2) {
// msg.sender is the alias of the Michelson account `native` (tz1… or KT1…)
}
```

### `resolveAddress`

`resolveAddress(addr, sourceRuntime, targetRuntime)` translates the address `addr` of the interface `sourceRuntime` into the corresponding address of the interface `targetRuntime`:

- If `addr` is malformed or Unknown, `classified` is `false` and the other values are zero.
- If `addr` is Native, `translated` is its alias in `targetRuntime`.
- If `addr` is an Alias, `translated` is the native address it stands for, rather than an alias of the alias.

`res` is `0` (Recorded) when the returned address already exists on chain and `1` (Derived) when it was computed with the derivation formula but no cross-interface call has created the alias yet. When `sourceRuntime` and `targetRuntime` are equal, a well-formed address is returned unchanged with `res == 0`.

```solidity
(bool classified, uint8 res, string memory evmAlias) = gateway.resolveAddress(
"tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb", 0, 1
);
// classified == true; evmAlias is the "0x…" EVM alias of the Tezos account;
// res == 1 until a cross-interface call creates the alias
```

## Failure behavior

### `callMichelson`
Expand Down Expand Up @@ -122,6 +185,10 @@ Because `callMichelsonView` must be invoked via `staticcall`, catch failures wit
// success == false if the Michelson view reverted or was not found
```

### `originOf` and `resolveAddress`

Malformed addresses never revert: they are reported as Unknown (`kind == 0`) or unclassified (`classified == false`). Both functions revert with the custom error `InvalidRuntimeId(uint8)` when a runtime id is neither `0` nor `1`.

### Infrastructure failures

A 5xx response from the Michelson runtime indicates a kernel-internal error (storage I/O failure, host fault). This is treated as a block-level abort rather than a catchable revert, meaning the entire block is rolled back. These failures are not caused by contract logic and are not catchable by EVM code.
34 changes: 34 additions & 0 deletions docs/michelson/developing/rpc-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,37 @@ The following L1 RPCs exhibit different behavior in Etherlink<!--TX-->, either t
| `GET /context/issuance/expected_issuance` | Returns dummy zero rewards; Etherlink has no token issuance. |
| `POST /helpers/scripts/pack_data` | Uses a throwaway dummy context instead of the live chain state; results may differ for gas-sensitive encodings. |
| `GET /context/constants` | Several constants differ from mainnet: `minimal_block_delay` = 1 s (the protocol encoding cannot express sub-second periods; actual block cadence follows Etherlink<!--TX--> blocks, down to 500 ms under load); `hard_gas_limit_per_operation` = 660,000 gas (the 30M EVM per-transaction gas cap converted at 22 milligas per EVM gas unit); `cost_per_byte` = 1 mutez. |

## Alias RPCs

EVM nodes<!--TXN--> also expose two Etherlink<!--TX-->-specific JSON-RPC methods that compute the alias of an address (see [Accounts and Aliases](/overview/accounts-and-aliases#aliases)). They are served on the node's JSON-RPC endpoint (the base URL, alongside the `eth_*` methods), not under `/tezlink`. The alias is derived from the address alone, so these methods work for any address, whether or not it has been used on chain.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All RPCs served by the node are Tezos X-specific, no need to mention that (and indeed, these don't seem to be EVM-specific):

Suggested change
EVM nodes<!--TXN--> also expose two Etherlink<!--TX-->-specific JSON-RPC methods that compute the alias of an address (see [Accounts and Aliases](/overview/accounts-and-aliases#aliases)). They are served on the node's JSON-RPC endpoint (the base URL, alongside the `eth_*` methods), not under `/tezlink`. The alias is derived from the address alone, so these methods work for any address, whether or not it has been used on chain.
EVM nodes<!--TXN--> also expose two JSON-RPC methods that compute the alias of an address (see [Accounts and Aliases](/overview/accounts-and-aliases#aliases)). They are served on the node's JSON-RPC endpoint (the base URL, alongside the `eth_*` methods), not under `/tezlink`. The alias is derived from the address alone, so these methods work for any address, whether or not it has been used on chain.


| Method | Parameter | Result |
|---|---|---|
| `tez_getTezosEthereumAddress` | A Michelson address (`tz1…`, `tz2…`, `tz3…`, or `KT1…`) | Its EVM alias (`0x…`) |
| `tez_getEthereumTezosAddress` | An EVM address (`0x…`) | Its Michelson alias (`KT1…`) |

For example:

```bash
curl --request POST \
--url http://localhost:8545 \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"id": 1,
"jsonrpc": "2.0",
"method": "tez_getEthereumTezosAddress",
"params": ["0x1234567890abcdef1234567890abcdef12345678"]
}
'
```

```json
{
"jsonrpc": "2.0",
"result": "KT1CYcsqu3TnW3aA2hYL62ZCtcA484yCG4Zq",
"id": 1
}
```
52 changes: 52 additions & 0 deletions docs/michelson/nac-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,54 @@ IF_NONE
{ … } (* use the returned bytes *)
```

## Address translation

Cross-interface calls run under the caller's alias (see [Accounts and Aliases](/overview/accounts-and-aliases)). The gateway exposes two on-chain views to translate between native addresses and aliases. Both take the address as a `string` in its printable form (base58check for the Michelson interface, `0x…` hex for the EVM interface) rather than as a Michelson `address`, and identify interfaces by a `nat` runtime id: `0` for the Michelson interface, `1` for the EVM interface. Michelson has no instruction to convert an `address` to a `string`, so the address to translate must already be available as a `string`, for example as a parameter.

| View | Parameter type | Return type |
|---|---|---|
| `originOf` | `pair string nat` (address, source runtime) | `or unit (or nat (pair nat string))` |
| `resolveAddress` | `pair string (pair nat nat)` (address, source runtime, target runtime) | `option (pair nat string)` |

### `originOf`

`originOf` returns how the address is classified in the source interface:

| Result | Meaning |
|---|---|
| `Left Unit` | Unknown: the address is malformed or has not been seen by the kernel yet |
| `Right (Left n)` | Native: an account native to the source interface (`n` is the source runtime id) |
| `Right (Right (Pair n addr))` | Alias: the alias of the account `addr`, native to the interface `n` |

The kernel records the origin of an account when it is first used: user accounts (`tz1…`, `tz2…`, `tz3…`) are always Native, smart contracts are recorded at origination, EVM accounts when they first sign a transaction or when they have code, and aliases when a cross-interface call creates them. In particular, an EVM address that has only received funds is Unknown.

### `resolveAddress`

`resolveAddress` translates the address from the source interface into the corresponding address of the target interface:

| Result | Meaning |
|---|---|
| `None` | The address is malformed or Unknown in the source interface |
| `Some (Pair 0 addr)` | Recorded: `addr` already exists on chain |
| `Some (Pair 1 addr)` | Derived: `addr` was computed with the derivation formula, but no cross-interface call has created the alias yet |

For a Native address, `addr` is its alias in the target interface. For an Alias, `addr` is the native address it stands for, rather than an alias of the alias. When the source and target runtimes are equal, a well-formed address is returned unchanged as Recorded.

```michelson
PUSH address "KT18oDJJKXMKhfE1bSuAPGp92pYcwVDiqsPw"; (* the gateway *)
PUSH nat 0; (* target runtime: Michelson *)
PUSH nat 1; (* source runtime: EVM *)
PAIR;
PUSH string "0x1234567890abcdef1234567890abcdef12345678";
PAIR; (* pair string (pair nat nat) *)
VIEW "resolveAddress" (option (pair nat string));
IF_NONE
{ FAIL } (* view not found *)
{ IF_NONE
{ … } (* unknown address *)
{ … } } (* Pair 0 "KT1CYcsqu3TnW3aA2hYL62ZCtcA484yCG4Zq" if the alias exists, Pair 1 … otherwise *)
```

## Return value

### `%call_evm` — callback
Expand Down Expand Up @@ -265,3 +313,7 @@ For the gas conversion rules and how the forwarded budget is calculated, see [Re
### `staticcall_evm`

A view failure (EVM revert, missing view, type mismatch) surfaces as `None` from `VIEW`, which the caller handles with `IF_NONE`. Out-of-gas is the exception: it fails the operation outright rather than returning `None`, so a forwarded-gas exhaustion cannot be silently treated as a missing view. See the outcome table in the [`staticcall_evm`](#staticcall_evm) section above.

### `originOf` and `resolveAddress`

Malformed addresses never fail: they are reported as Unknown (`Left Unit`) or `None`. Both views fail the operation with `(Pair "INVALID_RUNTIME_ID" n)` when a runtime id `n` is neither `0` nor `1`.
6 changes: 6 additions & 0 deletions docs/overview/accounts-and-aliases.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ kt1_alias = KT1(blake2b_160(utf8("0x" + lowercase_hex(evm_address))))

Michelson's `SOURCE` instruction returns the null address (`tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU`) for operations originating from cross-interface calls — not the actual alias address. This is because Michelson requires the source to be a user account.

## Translating addresses

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be part of the same section as the 2 above subsections:

Suggested change
## Translating addresses
### Translating addresses


Off chain, EVM nodes<!--TXN--> expose two JSON-RPC methods that apply the formulas above: `tez_getTezosEthereumAddress` and `tez_getEthereumTezosAddress`. See [Alias RPCs](/michelson/developing/rpc-reference#alias-rpcs).

On chain, both gateways expose `originOf` and `resolveAddress`, which consult the kernel's records rather than only applying the formulas: they tell whether an address is a native account or an alias, map an alias back to the native account it stands for, and tell whether an alias has already been created. See [NAC usage: EVM to Michelson](/evm/nac-usage#address-translation) and [NAC usage: Michelson to EVM](/michelson/nac-usage#address-translation).

## Tez precision across interfaces

The two interfaces use different precision for tez:
Expand Down