Guides
EVM
Use the Bittensor EVM from the terminal — keys, funding, MetaMask, precompiles, and the ss58 ↔ h160 seam.
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
Create a key, fund it, check balance — five commands.
Money flows
Four ways TAO crosses the ss58 ↔ EVM seam (with diagram).
Command reference
Every btcli evm subcommand, grouped by task.
Precompiles
Chain operations exposed as EVM contracts.
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 doctorConfirms 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_coldkeyCreates 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_coldkeyfund 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_coldkey4. (Optional) Connect MetaMask
btcli evm config --format metamaskPaste 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 …
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 sendMoney 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- 1.
Create or import an EVM key
btcli evm key new
- 2.
Coldkey transfers TAO to the key’s ss58 mirror
Wraps fund_evm_key intent
- 3.
Balance shows in MetaMask / btcli evm balance
18-decimal EVM view
Send between EVM accounts
btcli evm send- 1.
Pick source key (--evm-key)
Defaults to wallet default key
- 2.
Ordinary value transfer to another 0x address
Like Ethereum send
EVM key → any ss58 address
btcli evm send-to-ss58- 1.
Call BalanceTransfer precompile with msg.value
Destination needs no setup
- 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- 1.
Show your deposit address
btcli evm deposit-address
- 2.
Send TAO from MetaMask to that 0x address
Credits the truncated mirror
- 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
| 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 |
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_coldkeyWorked 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_coldkeybtcli 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 …)
| 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
| 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
| 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). |
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). |
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:
- Reads substrate block height (not EVM RPC).
- Unlocks the EVM key and produces an EIP-191 signature over
hotkey_pubkey (32B) ++ keccak(SCALE-u64(block_number)). - Submits
associate-evm-keysigned by the hotkey.
Query the link with associated-evm-key.
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:
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 2Setup & 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
| 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 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 defaultThe 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 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
bytes32public keys, not ss58 strings.btcli evm callconverts 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_precompileSubstrate-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_estimateGasfailures 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 addfor EVM-key-funded stake.