|
| 1 | +# Collateral Contract |
| 2 | + |
| 3 | +## Bittensor EVM Compatibility Layer |
| 4 | + |
| 5 | +Bittensor runs on **Subtensor**, a Substrate-based blockchain. The EVM layer is built using Frontier (Substrate's EVM compatibility framework) and runs **on top of** Subtensor as an application layer. All execution happens on the Bittensor blockchain, not Ethereum. |
| 6 | + |
| 7 | +### Two Address Worlds |
| 8 | + |
| 9 | +| Property | Substrate Side | EVM Side | |
| 10 | +|---|---|---| |
| 11 | +| **Format** | SS58 (starts with `5`) | H160 (starts with `0x`) | |
| 12 | +| **Size** | 32-byte public key (AccountId32) | 20-byte address | |
| 13 | +| **Key type** | Ed25519/Sr25519 | Secp256k1 | |
| 14 | +| **Wallet tools** | btcli, Bittensor SDK, Polkadot.js | MetaMask, Hardhat, ethers.js | |
| 15 | +| **Can do** | Subtensor extrinsics (staking, registration, transfers) | EVM smart contract calls | |
| 16 | +| **Cannot do** | Sign EVM smart contracts | Sign Substrate extrinsics | |
| 17 | + |
| 18 | +### HashedAddressMapping (H160 <-> SS58) |
| 19 | + |
| 20 | +Bittensor uses Frontier's `HashedAddressMapping` for deterministic, one-way address derivation: |
| 21 | + |
| 22 | +**H160 -> SS58 (EVM address -> Substrate mirror):** |
| 23 | +```rust |
| 24 | +fn into_account_id(address: H160) -> AccountId32 { |
| 25 | + let mut data = [0u8; 24]; |
| 26 | + data[0..4].copy_from_slice(b"evm:"); |
| 27 | + data[4..24].copy_from_slice(&address[..]); |
| 28 | + let hash = blake2_256(&data); |
| 29 | + AccountId32::from(hash) |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +**SS58 -> H160 (Substrate address -> EVM mirror):** |
| 34 | +Take the first 20 bytes of the 32-byte Substrate public key. |
| 35 | + |
| 36 | +**Critical:** Neither direction yields a usable private key. The derived "mirror" addresses are accounting-only. |
| 37 | + |
| 38 | +### Four Addresses, Two Keypairs |
| 39 | + |
| 40 | +``` |
| 41 | +Keypair A (Ed25519/Sr25519 - Bittensor native): |
| 42 | + +-- #1: Native SS58 address (you control, signs extrinsics) |
| 43 | + +-- #4: EVM mirror (first 20 bytes of pubkey, NO private key) |
| 44 | +
|
| 45 | +Keypair B (Secp256k1 - Ethereum native): |
| 46 | + +-- #3: Native H160 address (you control, signs EVM txns) |
| 47 | + +-- #2: SS58 mirror (blake2("evm:" ++ h160_bytes), NO private key) |
| 48 | +``` |
| 49 | + |
| 50 | +An SS58 wallet CANNOT sign EVM transactions. An EVM wallet CANNOT sign Substrate extrinsics. They are separate identity domains on the same chain. |
| 51 | + |
| 52 | +### Balance Flow Between Layers |
| 53 | + |
| 54 | +- Sending TAO from Substrate wallet (#1) to EVM mirror SS58 address (#2) makes it appear in the EVM wallet (#3) in MetaMask. |
| 55 | +- Going EVM -> Substrate uses the `BalanceTransfer` precompile or `evm.withdraw()` extrinsic. |
| 56 | +- Same TAO token, different account format, no wrapping involved. |
| 57 | + |
| 58 | +### Precompiles (EVM -> Substrate Bridge) |
| 59 | + |
| 60 | +| Precompile | Address | Purpose | |
| 61 | +|---|---|---| |
| 62 | +| Ed25519Verify | `0x...0402` | Verify Ed25519 signatures (prove SS58 key ownership from EVM) | |
| 63 | +| StakingV2 | `0x...0805` | Add/remove stake, move stake between hotkeys | |
| 64 | +| BalanceTransfer | custom | Transfer TAO between accounts | |
| 65 | +| SubnetPrecompile | custom | Subnet operations | |
| 66 | + |
| 67 | +**Staking caveat:** When a smart contract calls the staking precompile, the **contract's address** is the coldkey (not the original caller). |
| 68 | + |
| 69 | +### Network Config |
| 70 | + |
| 71 | +| Network | RPC URL | Chain ID | |
| 72 | +|---|---|---| |
| 73 | +| Mainnet | `https://lite.chain.opentensor.ai` | 964 | |
| 74 | +| Testnet | `https://test.finney.opentensor.ai` | 945 | |
| 75 | +| Localnet | `http://localhost:9944` | 42 | |
| 76 | + |
| 77 | +### Unit Conversion |
| 78 | + |
| 79 | +- EVM side: 1 TAO = 1e18 (like ETH wei) |
| 80 | +- Substrate staking (RAO): 1 TAO = 1e9 RAO |
| 81 | +- When calling staking precompile from EVM with `msg.value`: `amount_rao = msg.value / 1e9` |
| 82 | + |
| 83 | +## This Crate |
| 84 | + |
| 85 | +### Architecture |
| 86 | + |
| 87 | +- **Solidity contracts** (`src/Collateral.sol`, `src/CollateralUpgradeableV2.sol`): Upgradeable ERC1967 proxy pattern via OpenZeppelin |
| 88 | +- **Rust library** (`src/lib.rs`): Alloy-based contract bindings generated via `sol!` macro from ABI JSON |
| 89 | +- **CLI** (`src/main.rs`): `collateral-cli` for all contract operations (deposit, reclaim, slash, query, events) |
| 90 | + |
| 91 | +### Contract Identity Model |
| 92 | + |
| 93 | +The contract uses **H160 addresses** for miners and **bytes32** for hotkeys/coldkeys (which are Substrate public keys passed as raw 32-byte values). The `nodeId` is `bytes16` (UUID). The trustee is an H160 address. |
| 94 | + |
| 95 | +Both miners and validators need TAO in their H160 wallets for gas (~0.01 TAO minimum). |
| 96 | + |
| 97 | +### Key Contract State |
| 98 | + |
| 99 | +- `nodeToMiner[hotkey][nodeId] -> address`: Maps (hotkey, nodeId) to the miner's H160 address |
| 100 | +- `collaterals[hotkey][nodeId] -> uint256`: TAO collateral amount (in wei, 1e18) |
| 101 | +- `alphaCollaterals[hotkey][nodeId] -> uint256`: Alpha token collateral |
| 102 | +- `reclaims[reclaimId] -> Reclaim`: Pending reclaim requests with deny timeout |
| 103 | +- `CONTRACT_COLDKEY`: bytes32 Substrate coldkey associated with the contract (for staking precompile calls) |
| 104 | + |
| 105 | +### Contract Operations |
| 106 | + |
| 107 | +- **Deposit**: Miner sends TAO + optional alpha to lock as collateral for a (hotkey, nodeId) pair |
| 108 | +- **Reclaim**: Miner requests collateral back, starts a timeout window for trustee to deny |
| 109 | +- **Finalize Reclaim**: After timeout passes without denial, miner withdraws |
| 110 | +- **Deny Reclaim**: Trustee rejects a reclaim within the timeout window |
| 111 | +- **Slash**: Trustee slashes a miner's collateral for misconduct |
| 112 | +- **Burn Register**: Calls the `0x0804` precompile to burn-register the contract on-chain |
| 113 | + |
| 114 | +### Deployment |
| 115 | + |
| 116 | +- Mainnet deployment is **whitelisted only** (request via Bittensor Discord `#evm-bittensor`) |
| 117 | +- Uses `forge script script/DeployUpgradeable.s.sol` with ERC1967 proxy |
| 118 | +- After deployment, update ABI via `update_abi.py` |
| 119 | + |
| 120 | +### Testing |
| 121 | + |
| 122 | +```bash |
| 123 | +forge test # Solidity contract tests |
| 124 | +cargo test --lib # Rust library unit tests |
| 125 | +cargo test # All tests |
| 126 | +``` |
0 commit comments