# EVM (/docs/guides/evm)

Subtensor runs a full EVM runtime: standard smart contracts, standard
`eth_*` JSON-RPC, MetaMask, Hardhat, and Remix all work. Contracts execute
on the Bittensor chain (not Ethereum) with **TAO** as the native currency.

This guide starts with the smallest working path — create a key, fund it,
check the balance — then walks through every `btcli evm` command and the
deeper concepts (address mappings, decimals, precompiles).

<Cards>
  <Card title="Quick start" description="Create a key, fund it, check balance — five commands." href="#quick-start-your-first-evm-key" />

  <Card title="Money flows" description="Four ways TAO crosses the ss58 ↔ EVM seam (with diagram)." href="#money-flows" />

  <Card title="Command reference" description="Every btcli evm subcommand, grouped by task." href="#btcli-evm-command-reference" />

  <Card title="Precompiles" description="Chain operations exposed as EVM contracts." href="#precompiles" />
</Cards>

## Quick start: your first EVM key [#quick-start-your-first-evm-key]

**Prerequisites:** `bittensor[cli]` installed (includes `eth-account` for
EVM signing). A configured coldkey wallet (`btcli wallet list`).

### 1. Check the EVM endpoint [#1-check-the-evm-endpoint]

```bash
btcli evm doctor
```

Confirms the JSON-RPC URL, chain ID, gas price, and (if you have one) your
default EVM key's balance. On localnet, start the chain first; if chain ID is
unset, see [Connectivity](#connectivity) below.

### 2. Create an encrypted EVM key [#2-create-an-encrypted-evm-key]

```bash
btcli evm key new -w my_coldkey
btcli evm key list -w my_coldkey
```

Creates a random secp256k1 key stored as **Ethereum keystore V3** JSON next to
your hotkeys (`~/.bittensor/wallets/<wallet>/evmkeys/`). The file imports
directly into MetaMask (`btcli evm key export`).

<Callout>
  The default EVM key is **not** derived from your coldkey mnemonic — its
  compromise surface (browser wallets, dapp signing) stays separate from the
  coldkey seed. Use `btcli evm key import --mnemonic` only if you manage that
  seed yourself.
</Callout>

### 3. Fund the key from your coldkey [#3-fund-the-key-from-your-coldkey]

```bash
btcli evm fund --amount-tao 1 -w my_coldkey
btcli evm balance -w my_coldkey
```

`fund` transfers TAO from the coldkey to the EVM key's **ss58 mirror**
(substrate extrinsic, coldkey-signed). The balance then appears on the EVM
side — MetaMask shows it with **18 decimals** (see [Decimals](#the-decimals-trap)).

Preview first with `--dry-run`:

```bash
btcli evm fund --amount-tao 1 --dry-run -w my_coldkey
```

### 4. (Optional) Connect MetaMask [#4-optional-connect-metamask]

```bash
btcli evm config --format metamask
```

Paste the network settings into MetaMask (Add network → Add manually). Import
the keystore with `btcli evm key export --out ./mykey.json`.

You now have a funded EVM account on Subtensor. The sections below explain
*why* funding uses a mirror address, what every other command does, and how
to move TAO back to ss58.

## Two address domains [#two-address-domains]

<EvmAddressDomains />

**Takeaways:**

* **ss58** (`5…`) — native accounts; sign extrinsics with sr25519/ed25519.
* **h160** (`0x…`) — EVM accounts; sign transactions with secp256k1.
* **Mirror mapping** — fund an arbitrary EVM address: `btcli evm mirror 0x…`
* **Truncated mapping** — claim a MetaMask send into your coldkey:
  `btcli evm deposit-address`

Utility commands:

```bash
btcli evm mirror 0x742d35Cc6634C0532925a3b844Bc454e4438f44e   # ss58 mirror of an h160
btcli evm pubkey 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY  # bytes32 for precompiles
btcli evm deposit-address -w my_coldkey                         # where MetaMask should send
```

## Money flows [#money-flows]

<EvmMoneyFlows />

### Naming cheat sheet [#naming-cheat-sheet]

| Goal                       | Command                   | Who signs | Pays gas  |
| -------------------------- | ------------------------- | --------- | --------- |
| Coldkey → EVM key balance  | `btcli evm fund`          | coldkey   | substrate |
| EVM → EVM                  | `btcli evm send`          | EVM key   | EVM (wei) |
| EVM key → any ss58         | `btcli evm send-to-ss58`  | EVM key   | EVM (wei) |
| MetaMask deposit → coldkey | `btcli evm claim-deposit` | coldkey   | substrate |

<Callout type="warning">
  **Do not confuse** `send-to-ss58` with `claim-deposit`. The first spends from
  a **stored EVM key** via the BalanceTransfer precompile. The second pulls a
  **MetaMask deposit** into your coldkey via the substrate `EVM.withdraw`
  extrinsic (`btcli tx evm-withdraw` is the same intent).
</Callout>

### Worked example: fund → send → exit to ss58 [#worked-example-fund--send--exit-to-ss58]

```bash
# Fund
btcli evm fund --amount-tao 2 -w my_coldkey

# Send 0.5 TAO to another EVM address
btcli evm send --to 0xabc… --amount-tao 0.5 -w my_coldkey

# Send 0.5 TAO to a friend's ss58 (they need no EVM key)
btcli evm send-to-ss58 --to 5F… --amount-tao 0.5 -w my_coldkey
```

### Worked example: MetaMask → coldkey [#worked-example-metamask--coldkey]

```bash
btcli evm deposit-address -w my_coldkey
# Send TAO from MetaMask to the printed 0x deposit address

btcli evm claim-deposit --amount-tao 1 -w my_coldkey
```

## `btcli evm` command reference [#btcli-evm-command-reference]

All commands accept `-w` / `--wallet`, `-n` / `--network`, `--dry-run`,
`--yes`, and `--json` like the rest of btcli. EVM transaction commands
also accept `--rpc-url` to override the network's JSON-RPC endpoint.

### EVM keys (`btcli evm key …`) [#evm-keys-btcli-evm-key-]

| Command      | What it does                                                     |
| ------------ | ---------------------------------------------------------------- |
| `key new`    | Generate a random EVM key; store encrypted keystore V3.          |
| `key import` | Import from `--private-key`, `--keystore` file, or `--mnemonic`. |
| `key export` | Print or write keystore JSON (`--out` writes mode `0600`).       |
| `key list`   | Name, h160 address, and ss58 mirror for each stored key.         |
| `key show`   | Details for one key.                                             |

`--evm-key` elsewhere accepts a key **name** (`default`) or `WALLET/NAME`.

### Address helpers [#address-helpers]

| Command            | What it does                                                          |
| ------------------ | --------------------------------------------------------------------- |
| `mirror [ADDRESS]` | ss58 mirror of an h160 (where native TAO lands for that EVM account). |
| `pubkey SS58`      | 32-byte public key as 0x-hex (what precompiles expect as `bytes32`).  |
| `deposit-address`  | Truncated h160 + mirror for MetaMask → coldkey deposits.              |

### Money movement [#money-movement]

| Command                                 | What it does                                                               |
| --------------------------------------- | -------------------------------------------------------------------------- |
| `balance [ADDRESS]`                     | EVM balance in TAO and wei via JSON-RPC.                                   |
| `fund --amount-tao N`                   | Coldkey → EVM key mirror ([`fund-evm-key`](/docs/tx/fund-evm-key)).        |
| `send --to 0x… --amount-tao N`          | Ordinary EVM value transfer between h160 accounts.                         |
| `send-to-ss58 --to SS58 --amount-tao N` | EVM key → ss58 via BalanceTransfer precompile.                             |
| `claim-deposit --amount-tao N`          | Coldkey claims MetaMask deposit ([`evm-withdraw`](/docs/tx/evm-withdraw)). |

### Hotkey association [#hotkey-association]

| Command                | What it does                                                          |
| ---------------------- | --------------------------------------------------------------------- |
| `associate --netuid N` | Link stored EVM key to wallet hotkey on a subnet (proof + extrinsic). |

Some subnets require an associated EVM key. The command:

1. Reads substrate block height (not EVM RPC).
2. Unlocks the EVM key and produces an **EIP-191** signature over
   `hotkey_pubkey (32B) ++ keccak(SCALE-u64(block_number))`.
3. Submits [`associate-evm-key`](/docs/tx/associate-evm-key) signed by the hotkey.

Query the link with [`associated-evm-key`](/docs/query/associated-evm-key).

### Precompiles & chain [#precompiles--chain]

| Command                  | What it does                                                     |
| ------------------------ | ---------------------------------------------------------------- |
| `precompiles`            | Catalog: name, address, description.                             |
| `abi NAME`               | Address + ABI JSON for Hardhat/ethers/viem.                      |
| `call NAME [FN] [ARGS…]` | View calls free via `eth_call`; writes need `--evm-key`.         |
| `stake add/remove/show`  | Staking-v2 precompile sugar (TAO/alpha unit conversion handled). |

Examples:

```bash
btcli evm call metagraph getUidCount 1              # view — no key, no gas
btcli evm call staking-v2 getStake HOTKEY MIRROR 1  # view stake position
btcli evm stake add --netuid 1 --hotkey 5F… --amount-tao 2
```

### Setup & diagnosis [#setup--diagnosis]

| Command                    | What it does                                              |
| -------------------------- | --------------------------------------------------------- |
| `networks`                 | Chain ID and RPC URL per preset network.                  |
| `config --format metamask` | Paste-ready MetaMask / Hardhat / Remix settings.          |
| `doctor`                   | Probe RPC reachability, chain ID, gas price, key balance. |

## Connectivity [#connectivity]

| Network          | EVM RPC                            | Chain ID       |
| ---------------- | ---------------------------------- | -------------- |
| Mainnet (finney) | `https://lite.chain.opentensor.ai` | 964            |
| Testnet          | `https://test.chain.opentensor.ai` | 945            |
| Localnet         | `http://127.0.0.1:9944`            | none until set |

Mainnet is also on [ChainList](https://chainlist.org) as chain **964**. A fresh
localnet has no EVM chain ID — set one with the sudo extrinsic
`adminUtils.sudoSetEvmChainId` (945 for testnet-like, 964 for mainnet-like)
before MetaMask accepts the network.

Override the RPC for any command:

```bash
btcli evm balance --rpc-url http://127.0.0.1:9944
export BT_EVM_ENDPOINT=http://127.0.0.1:9944   # local preset default
```

## The decimals trap [#the-decimals-trap]

Native TAO has **9 decimals** (1 TAO = 1e9 rao). The EVM side uses Ethereum's
**18 decimals**: 1 TAO = 1e18 wei in a transaction's `value` field. MetaMask
always assumes 18, so displayed EVM balances use a different exponent than
substrate wallets — **the funds are the same**.

Contract code forwarding `msg.value` into a precompile must convert to rao:

```solidity
uint256 amountRao = msg.value / 1e9;
```

Precompile **amount parameters** are in rao, not 1e18 TAO.

## Precompiles [#precompiles]

Beyond standard Ethereum precompiles (ECRecover, Sha256, …), subtensor exposes
chain operations at fixed addresses (the 20-byte zero-padded **index** — e.g.
index `2053` → `0x…0805`):

| Name             | Index | Address | Purpose                                          |
| ---------------- | ----- | ------- | ------------------------------------------------ |
| balance-transfer | 2048  | `0x800` | Send TAO from EVM to any ss58 (`msg.value`).     |
| staking (v1)     | 2049  | `0x801` | Deprecated — use staking-v2.                     |
| metagraph        | 2050  | `0x802` | Read neuron/subnet state.                        |
| subnet           | 2051  | `0x803` | Subnet registration and owner params.            |
| neuron           | 2052  | `0x804` | Weights, registration, serving.                  |
| staking-v2       | 2053  | `0x805` | add/remove/move/transfer stake (amounts in rao). |
| uid-lookup       | 2054  | `0x806` | UIDs for an EVM address on a subnet.             |
| alpha            | 2056  | `0x808` | Subnet alpha token info.                         |
| crowdloan        | 2057  | `0x809` | Crowdloan from EVM.                              |
| leasing          | 2058  | `0x80a` | Subnet leasing.                                  |
| proxy            | 2059  | `0x80b` | Proxy delegations.                               |
| address-mapping  | 2060  | `0x80c` | On-chain h160 → mirror bytes32.                  |
| ed25519-verify   | 1026  | `0x402` | Verify ed25519 (prove ss58 ownership).           |
| sr25519-verify   | 1027  | `0x403` | Verify sr25519.                                  |

Semantics that surprise Ethereum developers:

* When a **contract** calls a precompile, the **contract address** is the
  coldkey — it must hold the funds.
* Hotkey/coldkey arguments are **`bytes32` public keys**, not ss58 strings.
  `btcli evm call` converts ss58 automatically; Solidity must pass raw keys.
* Amount parameters are in **rao** (see decimals above).

Source: [subtensor/precompiles](https://github.com/opentensor/subtensor/tree/main/precompiles).
Examples: [opentensor/evm-bittensor](https://github.com/opentensor/evm-bittensor/tree/main/examples).

## Python SDK [#python-sdk]

The same layer lives in `bittensor.evm` (install `bittensor[evm]`):

```python
import bittensor as sub
from bittensor.evm import h160_to_ss58, association_proof
from bittensor.evm.keys import create_evm_key, unlock_evm_key
from bittensor.evm.precompiles import encode_call, get_precompile
```

Substrate-side intents ([`fund-evm-key`](/docs/tx/fund-evm-key),
[`evm-withdraw`](/docs/tx/evm-withdraw),
[`associate-evm-key`](/docs/tx/associate-evm-key)) use the normal
`client.plan` / `client.execute` flow. EVM-side sends use `bittensor.evm.rpc`
and `bittensor.evm.transactions` directly.

## Hosted bridges [#hosted-bridges]

Third-party services (not part of this SDK):

* [tao.app/bridge](https://tao.app/bridge) — TAO between substrate and EVM wallets.
* vTAO — liquid-staked TAO ERC-20 on the Bittensor EVM, bridgeable to Base.

## Tooling notes [#tooling-notes]

* Compile with **Solidity 0.8.24 or lower, EVM target Cancun**. Newer targets
  can fail deploy with `InvalidCode(Opcode)` or gas-estimation errors.
* `eth_estimateGas` failures mean *any* invalid transaction — insufficient
  balance, bad calldata, unset chain ID, or localnet deployment whitelist —
  not just gas problems.
* MetaMask nonces stuck after a localnet restart: Settings → Advanced →
  "Clear activity tab data".
* Localnet contract deployment may require disabling the whitelist via sudo
  `evm.disableWhitelist`.

## See also [#see-also]

* [Local development](/docs/guides/local-development) — run a chain for testing.
* [Transfer](/docs/tx/transfer) — send TAO to an EVM mirror manually.
* [Staking guide](/docs/guides/staking) — native-side stake; compare with
  `btcli evm stake add` for EVM-key-funded stake.
