Guides

EVM

Use the Bittensor EVM from the terminal — keys, funding, MetaMask, precompiles, and the ss58 ↔ h160 seam.

View as Markdown

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).

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

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 below.

2. Create an encrypted EVM key

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).

3. Fund the key from your coldkey

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).

Preview first with --dry-run:

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

4. (Optional) Connect MetaMask

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 signing domains, two address mappings

Conversions are deterministic but never carry private keys — a btcli wallet cannot sign EVM txs, and MetaMask cannot sign extrinsics.

Native (ss58)

5GrwvaEF…

sr25519 / ed25519 extrinsics

Coldkeys, hotkeys, neurons

Used by btcli tx …

same chain

EVM (h160)

0x742d35Cc…

secp256k1 EVM transactions

MetaMask, Hardhat, contracts

Used by eth_sendRawTransaction

Hashed mirror (fund an EVM account)

Every h160 has an ss58 mirror: ss58(blake2("evm:" ++ h160)). Transfer TAO to the mirror and it appears as that EVM account's balance.

btcli evm mirror · btcli evm fund

Truncated mapping (claim a MetaMask deposit)

Every ss58 account controls one h160: the first 20 bytes of its public key. Send TAO from MetaMask to that address, then claim it on the native side.

btcli evm deposit-address · btcli evm claim-deposit

Native decimals

1 TAO = 1e9 rao

EVM decimals

1 TAO = 1e18 wei

Same funds, different display scale

Key rule

Never mixed

Fund with mirror; claim with truncated

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:

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

Four ways TAO crosses the ss58 ↔ EVM seam

Pick the path that matches who holds the keys. The two “withdraw” names are easy to confuse — read the signer and gas columns.

Fund an EVM key from your coldkey

btcli evm fund
signs: coldkey (substrate)gas: substrate fee only
  1. 1.

    Create or import an EVM key

    btcli evm key new

  2. 2.

    Coldkey transfers TAO to the key’s ss58 mirror

    Wraps fund_evm_key intent

  3. 3.

    Balance shows in MetaMask / btcli evm balance

    18-decimal EVM view

Send between EVM accounts

btcli evm send
signs: stored EVM keygas: EVM gas (wei)
  1. 1.

    Pick source key (--evm-key)

    Defaults to wallet default key

  2. 2.

    Ordinary value transfer to another 0x address

    Like Ethereum send

EVM key → any ss58 address

btcli evm send-to-ss58
signs: stored EVM keygas: EVM gas (wei)
  1. 1.

    Call BalanceTransfer precompile with msg.value

    Destination needs no setup

  2. 2.

    TAO credits the ss58 account natively

    Not the same as claim-deposit

Formerly named evm withdraw — renamed to avoid confusion with claim-deposit.

MetaMask deposit → coldkey

btcli evm claim-deposit
signs: coldkey (substrate)gas: substrate fee only
  1. 1.

    Show your deposit address

    btcli evm deposit-address

  2. 2.

    Send TAO from MetaMask to that 0x address

    Credits the truncated mirror

  3. 3.

    Pull funds into the coldkey

    Also: btcli tx evm-withdraw

Uses the truncated mapping (first 20 bytes of pubkey), not the hashed mirror.

Naming cheat sheet

GoalCommandWho signsPays gas
Coldkey → EVM key balancebtcli evm fundcoldkeysubstrate
EVM → EVMbtcli evm sendEVM keyEVM (wei)
EVM key → any ss58btcli evm send-to-ss58EVM keyEVM (wei)
MetaMask deposit → coldkeybtcli evm claim-depositcoldkeysubstrate

Worked example: fund → send → exit to ss58

# 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

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

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 …)

CommandWhat it does
key newGenerate a random EVM key; store encrypted keystore V3.
key importImport from --private-key, --keystore file, or --mnemonic.
key exportPrint or write keystore JSON (--out writes mode 0600).
key listName, h160 address, and ss58 mirror for each stored key.
key showDetails for one key.

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

Address helpers

CommandWhat it does
mirror [ADDRESS]ss58 mirror of an h160 (where native TAO lands for that EVM account).
pubkey SS5832-byte public key as 0x-hex (what precompiles expect as bytes32).
deposit-addressTruncated h160 + mirror for MetaMask → coldkey deposits.

Money movement

CommandWhat it does
balance [ADDRESS]EVM balance in TAO and wei via JSON-RPC.
fund --amount-tao NColdkey → EVM key mirror (fund-evm-key).
send --to 0x… --amount-tao NOrdinary EVM value transfer between h160 accounts.
send-to-ss58 --to SS58 --amount-tao NEVM key → ss58 via BalanceTransfer precompile.
claim-deposit --amount-tao NColdkey claims MetaMask deposit (evm-withdraw).

Hotkey association

CommandWhat it does
associate --netuid NLink 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 signed by the hotkey.

Query the link with associated-evm-key.

Precompiles & chain

CommandWhat it does
precompilesCatalog: name, address, description.
abi NAMEAddress + ABI JSON for Hardhat/ethers/viem.
call NAME [FN] [ARGS…]View calls free via eth_call; writes need --evm-key.
stake add/remove/showStaking-v2 precompile sugar (TAO/alpha unit conversion handled).

Examples:

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

CommandWhat it does
networksChain ID and RPC URL per preset network.
config --format metamaskPaste-ready MetaMask / Hardhat / Remix settings.
doctorProbe RPC reachability, chain ID, gas price, key balance.

Connectivity

NetworkEVM RPCChain ID
Mainnet (finney)https://lite.chain.opentensor.ai964
Testnethttps://test.chain.opentensor.ai945
Localnethttp://127.0.0.1:9944none until set

Mainnet is also on ChainList 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:

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

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:

uint256 amountRao = msg.value / 1e9;

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

Precompiles

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

NameIndexAddressPurpose
balance-transfer20480x800Send TAO from EVM to any ss58 (msg.value).
staking (v1)20490x801Deprecated — use staking-v2.
metagraph20500x802Read neuron/subnet state.
subnet20510x803Subnet registration and owner params.
neuron20520x804Weights, registration, serving.
staking-v220530x805add/remove/move/transfer stake (amounts in rao).
uid-lookup20540x806UIDs for an EVM address on a subnet.
alpha20560x808Subnet alpha token info.
crowdloan20570x809Crowdloan from EVM.
leasing20580x80aSubnet leasing.
proxy20590x80bProxy delegations.
address-mapping20600x80cOn-chain h160 → mirror bytes32.
ed25519-verify10260x402Verify ed25519 (prove ss58 ownership).
sr25519-verify10270x403Verify 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. Examples: opentensor/evm-bittensor.

Python SDK

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

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, evm-withdraw, 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

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

  • tao.app/bridge — TAO between substrate and EVM wallets.
  • vTAO — liquid-staked TAO ERC-20 on the Bittensor EVM, bridgeable to Base.

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

  • Local development — run a chain for testing.
  • Transfer — send TAO to an EVM mirror manually.
  • Staking guide — native-side stake; compare with btcli evm stake add for EVM-key-funded stake.