diff --git a/docs/bittensor-networks.md b/docs/bittensor-networks.md
index fefbd67db..1d24d1d69 100644
--- a/docs/bittensor-networks.md
+++ b/docs/bittensor-networks.md
@@ -9,7 +9,7 @@ The below table presents Bittensor networks and a few details:
| DESCRIPTION | MAINNET | TESTNET | DEVNET |
|:---------------------|:------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------|
| **Chain URL** | wss://entrypoint-finney.opentensor.ai:443 | wss://test.finney.opentensor.ai:443 | wss://dev.chain.opentensor.ai:443 |
-| **Example Usage** | Default | `btcli wallet swap_hotkey --subtensor.chain_endpoint wss://dev.chain.opentensor.ai:443` **or** `btcli wallet swap_hotkey --subtensor.network test` | `btcli wallet swap_hotkey --subtensor.chain_endpoint wss://dev.chain.opentensor.ai:443` |
+| **Example Usage** | Default | `btcli wallet swap_hotkey --network wss://dev.chain.opentensor.ai:443` | `btcli wallet swap_hotkey --network wss://dev.chain.opentensor.ai:443` |
| **Block processing** | One block every 12 seconds | One block every 12 seconds | One block every 12 seconds |
| **Mainnet Archive** | wss://archive.chain.opentensor.ai:443 | None | None |
| **Mainnet Lite** | wss://lite.chain.opentensor.ai:443 | None | None |
diff --git a/docs/evm-tutorials/_create-btcli-wallet.mdx b/docs/evm-tutorials/_create-btcli-wallet.mdx
new file mode 100644
index 000000000..22300a5e4
--- /dev/null
+++ b/docs/evm-tutorials/_create-btcli-wallet.mdx
@@ -0,0 +1,32 @@
+import React from 'react';
+
+export const CreateBtcliPartial = () => (
+ <>
+
Create Wallet with BTCLI
+
+
+ - Install BTCLI if you haven't already:
+
+
+ pip install bittensor
+
+
+
+ - Create a new wallet:
+
+
+ btcli wallet new_coldkey --wallet.name my_wallet
+
+
+
+ - Note down your wallet's SS58 address, which you can get with:
+
+
+ btcli wallet overview --wallet.name my_wallet
+
+
+
+ - Your coldkey address will start with "5" and is in SS58 format (for example:
5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty
).
+
+ >
+);
\ No newline at end of file
diff --git a/docs/evm-tutorials/_create-mm-wallet.mdx b/docs/evm-tutorials/_create-mm-wallet.mdx
new file mode 100644
index 000000000..60c061a79
--- /dev/null
+++ b/docs/evm-tutorials/_create-mm-wallet.mdx
@@ -0,0 +1,23 @@
+import React from 'react';
+
+export const CreatePartial = () => (
+ <>
+ Create Wallet with MetaMask
+
+
+ - Install Metamask wallet browser extension, if you haven't already.
+ - Create a new account or import an existing one.
+ - Add the Bittensor EVM network to MetaMask:
+
+ - Network Name: Bittensor EVM
+ - RPC URL: https://test.chain.opentensor.ai
+ - Chain ID: 945
+ - Currency Symbol: TAO
+ - Block Explorer URL: test.chain.opentensor.ai
+
+
+ - Click Save.
+ - Click Switch network.
+
+ >
+);
\ No newline at end of file
diff --git a/docs/evm-tutorials/_install.mdx b/docs/evm-tutorials/_install.mdx
new file mode 100644
index 000000000..e8dbe0d8d
--- /dev/null
+++ b/docs/evm-tutorials/_install.mdx
@@ -0,0 +1,34 @@
+export const InstallPartial = () => (
+ <>
+ Install the EVM Examples repo
+
+ -
+ Clone the Opentensor EVM-Bittensor GitHub repo:
+
+
+ git clone https://github.com/opentensor/evm-bittensor.git
+
+
+
+
+ -
+ Navigate to
evm-bittensor
directory:
+
+
+ cd evm-bittensor
+
+
+
+
+ -
+ Install the dependencies:
+
+
+ npm install
+
+
+
+
+ >
+
+);
\ No newline at end of file
diff --git a/docs/evm-tutorials/convert-h160-to-ss58.md b/docs/evm-tutorials/convert-h160-to-ss58.md
new file mode 100644
index 000000000..dda6e81ec
--- /dev/null
+++ b/docs/evm-tutorials/convert-h160-to-ss58.md
@@ -0,0 +1,88 @@
+---
+title: "Convert Ethereum (H160) Address to Substrate (SS58)"
+---
+import { InstallPartial } from "./_install.mdx";
+import { CreatePartial } from "./_create-mm-wallet.mdx";
+
+# Convert Ethereum (H160) Address to Substrate (SS58)
+
+This tutorial demonstrates how to convert between Ethereum (H160) and Substrate (SS58) addresses. This is useful for moving across the boundary between [EVM wallets and Subtensor Wallets on the Bittensor blockchain](./#evm-wallets-and-subtensor-wallets-on-the-bittensor-blockchain).
+
+In what follows, we'll create a wallet in Metamask and convert it's public key to ss58 format in order to target it with a balance transfer using BTCLI.
+
+## Procedure
+
+
+
+
+
+## Set your config
+
+### Convert Address for Bittensor
+
+Run the conversion script, replacing `ethereumAddress` with your address:
+
+```bash
+node convert-address.js
+```
+
+Note down the SS58 address output by the script - this is your wallet's Subtensor address on the Bittensor network.
+
+### Transfer TAO to EVM Wallet
+
+Use `btcli` to transfer TAO to your SS58 address. Here we will use test network.
+
+ ```bash
+ btcli wallet transfer --destination --network test
+ ```
+### Verify Balance in MetaMask
+
+1. Open MetaMask
+2. Ensure you're connected to the Bittensor EVM network
+3. Your TAO balance should now be visible in MetaMask
+4. You can now use this wallet for EVM transactions on Bittensor
+
+## Conversion Script
+
+Below is the code used above for the conversion.
+
+**Source code**:
+- [EVM examples repo](https://github.com/opentensor/evm-bittensor)
+- [Address mapping](https://github.com/opentensor/evm-bittensor/blob/main/examples/address-mapping.js)
+- [Convert address](https://github.com/opentensor/evm-bittensor/blob/main/examples/convert-address.js)
+
+```javascript
+//convert-address.js
+
+const { convertH160ToSS58 } = require('./address-mapping.js');
+
+async function main() {
+ const ethereumAddress = "0xbdA293c21DfCaDDAeB9aa8b98455d42325599d23";
+
+ const ss58Address = convertH160ToSS58(ethereumAddress);
+ console.log(`ss58 mirror: ${ss58Address}`);
+}
+
+main().catch(console.error);
+```
+
+```javascript
+// address-mapping.js
+function convertH160ToSS58(ethAddress) {
+ const prefix = 'evm:';
+ const prefixBytes = new TextEncoder().encode(prefix);
+ const addressBytes = hexToU8a(ethAddress.startsWith('0x') ? ethAddress : `0x${ethAddress}`);
+ const combined = new Uint8Array(prefixBytes.length + addressBytes.length);
+
+ // Concatenate prefix and Ethereum address
+ combined.set(prefixBytes);
+ combined.set(addressBytes, prefixBytes.length);
+
+ // Hash the combined data (the public key)
+ const hash = blake2AsU8a(combined);
+
+ // Convert the hash to SS58 format
+ const ss58Address = encodeAddress(hash, 42); // Network ID 42 for Bittensor
+ return ss58Address;
+}
+```
\ No newline at end of file
diff --git a/docs/evm-tutorials/ed25519-verify-precompile.md b/docs/evm-tutorials/ed25519-verify-precompile.md
index 1aae5a448..272e32896 100644
--- a/docs/evm-tutorials/ed25519-verify-precompile.md
+++ b/docs/evm-tutorials/ed25519-verify-precompile.md
@@ -1,23 +1,23 @@
---
-title: "Ed25519 Verify Precompile"
+title: "Verify Address Precompile"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# Ed25519 Verify Precompile
+# Verify Address Precompile
-This precompile is deployed on the subtensor EVM at the address `0x0000000000000000000000000000000000000402`. This precompile allows you to verify an `ed25519` signature.
+The Ed25519 Verify Precompile allows EVM smart contracts to verify Ed25519 signatures, which are commonly used in Substrate-based chains like Bittensor. This is essential for bridging identity and ownership between Substrate and EVM ecosystems. For example, you may want to verify coldkey ownership before transferring to someone. EVM functionality doesn't allow transferring directly to a `ss58` address—like the public key of a Bittensor coldkey—because EVM uses the H160 address schema. To bridge the gap, you can use this precompile to prove a claim of ownership. The owner of a coldkey can send an EVM transaction with a signed message, serving as proof of ownership of the coldkey's `ss58` address.
-You can use this precompile to verify proof of `ss58` account ownership on the EVM side. For example, you may need to do such verification for an airdrop to TAO owners. While EVM functionality doesn't allow airdropping directly to `ss58` addresses (because EVM is using H160 address schema), one can implement an airdrop via claiming. An owner of `ss58` address eligible for an airdrop can send an EVM transaction which includes the proof of `ss58` address ownership, for example, a signed message, uniquely specific for a given airdrop.
+## Prerequisites
-For a complete code example see [`ed25519-verify.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/ed25519-verify.js).
+- **Node.js** (v16 or later recommended)
+- **npm** or **yarn**
+- [Clone the Bittensor EVM examples repo](./install.md)
+- [Get set up for using EVM wallet on testnet](./evm-testnet-with-metamask-wallet)
+- [Install](./install) the EVM-Bittensor repo, containing scripts and examples.
-:::danger Stop. Did you install the dependencies?
-Before you proceed, make sure you finished the [Install](./install.md) step.
-:::
-
-## Run
+## Example
Navigate to the `examples` directory of the EVM-Bittensor repo:
@@ -33,15 +33,150 @@ To run this precompile, execute:
This example demonstrates how to:
1. Sign an arbitrary message with `ed25519` key.
+2. Verify the signature using the precompile contract.
+3. Fail the verification of the signature using a corrupted message hash with the precompile contract.
+4. Fail the verification of a corrupted signature with the precompile contract.
- Any substrate keyring can be initialized as `ed25519` with the same seed phrase or private key as used for signing subtensor transactions, even if they are usually used to create `sr25519` signatures.
-
- The precompile only allows verification of 32-byte messages. However, the arbitrary message can be converted into 32-byte message by calculating the message hash (like it is done in this below example):
- ```javascript
- const messageHash = ethers.keccak256(messageHex); // Hash the message to fit into bytes32
- ```
+[On GitHub](https://github.com/opentensor/evm-bittensor/blob/main/examples/ed25519-verify.js).
-2. Verify the signature using the precompile contract.
-3. Fail the verification of the signature using the corrupted message hash with the precompile contract.
-4. Fail the verification of the corrupted signature with the precompile contract.
\ No newline at end of file
+
+ Full code
+```js
+const { ethers } = require('ethers');
+const { Keyring } = require('@polkadot/keyring');
+
+// PROTECT YOUR PRIVATE KEYS WELL, NEVER COMMIT THEM TO GITHUB OR SHARE WITH ANYONE
+const { rpcUrl } = require('./config.js');
+
+const provider = new ethers.JsonRpcProvider(rpcUrl);
+
+const IED25519VERIFY_ADDRESS = '0x0000000000000000000000000000000000000402';
+const IEd25519VerifyABI = [
+ {
+ "inputs": [
+ { "internalType": "bytes32", "name": "message", "type": "bytes32" },
+ { "internalType": "bytes32", "name": "publicKey", "type": "bytes32" },
+ { "internalType": "bytes32", "name": "r", "type": "bytes32" },
+ { "internalType": "bytes32", "name": "s", "type": "bytes32" }
+ ],
+ "name": "verify",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "pure",
+ "type": "function"
+ }
+];
+
+async function main() {
+ const keyring = new Keyring({ type: 'ed25519' });
+ const myAccount = keyring.addFromUri('//Alice');
+
+ //////////////////////////////////////////////////////////////////////
+ // Generate a signature
+
+ // Your message to sign
+ const message = 'Sign this message';
+ const messageU8a = new TextEncoder().encode(message);
+ const messageHex = ethers.hexlify(messageU8a); // Convert message to hex string
+ const messageHash = ethers.keccak256(messageHex); // Hash the message to fit into bytes32
+ console.log(`messageHash = ${messageHash}`);
+ const hashedMessageBytes = hexToBytes(messageHash);
+
+ // Sign the message
+ const signature = myAccount.sign(hashedMessageBytes);
+ console.log(`Signature: ${bytesToHex(signature)}`);
+
+ // Verify the signature locally
+ const isValid = myAccount.verify(hashedMessageBytes, signature, myAccount.publicKey);
+ console.log(`Is the signature valid? ${isValid}`);
+
+ //////////////////////////////////////////////////////////////////////
+ // Verify the signature using the precompile contract
+
+ const publicKeyBytes = bytesToHex(myAccount.publicKey);
+ console.log(`publicKeyBytes = ${publicKeyBytes}`);
+
+ // Split signture into Commitment (R) and response (s)
+ let r = signature.slice(0, 32); // Commitment, a.k.a. "r" - first 32 bytes
+ let s = signature.slice(32, 64); // Response, a.k.a. "s" - second 32 bytes
+ let rBytes = bytesToHex(r);
+ let sBytes = bytesToHex(s);
+ const ed25519Contract = new ethers.Contract(IED25519VERIFY_ADDRESS, IEd25519VerifyABI, provider);
+ const isPrecompileValid = await ed25519Contract.verify(messageHash, publicKeyBytes, rBytes, sBytes);
+ console.log(`Is the signature valid according to the smart contract? ${isPrecompileValid}`);
+
+ //////////////////////////////////////////////////////////////////////
+ // Verify the signature for bad data using the precompile contract
+
+ let brokenHashedMessageBytes = hashedMessageBytes;
+ brokenHashedMessageBytes[0] = (brokenHashedMessageBytes[0] + 1) % 0xff;
+ const brokenMessageHash = bytesToHex(brokenHashedMessageBytes);
+ console.log(`brokenMessageHash = ${brokenMessageHash}`);
+ const isPrecompileValidBadData = await ed25519Contract.verify(brokenMessageHash, publicKeyBytes, rBytes, sBytes);
+ console.log(`Is the signature valid according to the smart contract for broken data? ${isPrecompileValidBadData}`);
+
+ //////////////////////////////////////////////////////////////////////
+ // Verify the bad signature for good data using the precompile contract
+
+ let brokenR = r;
+ brokenR[0] = (brokenR[0] + 1) % 0xff;
+ rBytes = bytesToHex(r);
+ const isPrecompileValidBadSignature = await ed25519Contract.verify(messageHash, publicKeyBytes, rBytes, sBytes);
+ console.log(`Is the signature valid according to the smart contract for broken signature? ${isPrecompileValidBadSignature}`);
+}
+
+main().catch(console.error);
+
+function hexToBytes(hex) {
+ // Remove the '0x' prefix if it exists
+ if (hex.startsWith('0x')) {
+ hex = hex.slice(2);
+ }
+
+ // Initialize the array
+ var bytes = new Uint8Array(hex.length / 2);
+
+ // Loop through each pair of characters
+ for (var i = 0; i < bytes.length; i++) {
+ // Convert the pair of characters to a byte
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
+ }
+
+ return bytes;
+}
+
+function bytesToHex(bytes) {
+ // Initialize the hex string
+ var hex = [];
+
+ // Loop through each byte
+ for (var i = 0; i < bytes.length; i++) {
+ // Convert each byte to a hex string and add it to the array
+ // Ensure it is two digits by padding with a zero if necessary
+ hex.push((bytes[i] >>> 4).toString(16));
+ hex.push((bytes[i] & 0xF).toString(16));
+ }
+
+ // Join all hex string parts into one string
+ return '0x' + hex.join('');
+}
+```
+
+## Example Output
+
+```
+node ed25519-verify.js
+@polkadot/util has multiple versions, ensure that there is only one installed.
+Either remove and explicitly install matching versions or dedupe using your package manager.
+The following conflicting packages were found:
+ cjs 12.2.1 node_modules/@polkadot/keyring/node_modules/@polkadot/util/cjs
+ cjs 13.5.1 node_modules/@polkadot/util/cjs
+messageHash = 0xd6ce89c7d4f347455c7dddf19b42e0357edd7587b73b81b384810253c3c3c8ff
+Signature: 0x35c3c28c3470ea348343cea4881bd353843236df73a04300261cb86411fe88a05a196842849eb1ef4335b1f171a70e74d2d4c8d3b71ad6a41b6fa48afec85b01
+Is the signature valid? true
+publicKeyBytes = 0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee
+Is the signature valid according to the smart contract? true
+brokenMessageHash = 0xd7ce89c7d4f347455c7dddf19b42e0357edd7587b73b81b384810253c3c3c8ff
+Is the signature valid according to the smart contract for broken data? false
+Is the signature valid according to the smart contract for broken signature? false
+```
\ No newline at end of file
diff --git a/docs/evm-tutorials/evm-localnet-with-metamask-wallet.md b/docs/evm-tutorials/evm-localnet-with-metamask-wallet.md
index 856493d9e..0b5857d7c 100644
--- a/docs/evm-tutorials/evm-localnet-with-metamask-wallet.md
+++ b/docs/evm-tutorials/evm-localnet-with-metamask-wallet.md
@@ -1,17 +1,15 @@
---
-title: "EVM Localnet with Metamask Wallet"
+title: "EVM on Local Chain"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# EVM Localnet with Metamask Wallet
+# EVM on Local Chain
-This tutorial is for how to set up your Metamask wallet to use with EVM localnet on Bittensor. You must run either this step or [EVM Testnet with Metamask Wallet](./evm-testnet-with-metamask-wallet.md) tutorial before you can run other tutorials in this section.
+This page covers getting set up to use EVM on a locally deployed Bittensor blockchain.
-:::tip blog post: EVM on Bittensor
-If you are new to EVM, try this [blog post](https://blog.bittensor.com/evm-on-bittensor-draft-6f323e69aff7) for a simplified explanation.
-:::
+Consider first trying [EVM with Bittensor testnet](./evm-testnet-with-metamask-wallet.md). This allows you to try EVM without having to deploy a blockchain locally, but you will have to obtain testnet TAO by inquiring in discord, or by completing the [BTCLI playground](../btcli/btcli-playground)challenge to obtain testnet TAO.
Key values:
- **EVM Subtensor Mainnet Chain ID:**: `964` (UTF-8 encoded TAO symbol)
diff --git a/docs/evm-tutorials/evm-mainnet-with-metamask-wallet.md b/docs/evm-tutorials/evm-mainnet-with-metamask-wallet.md
index 7a7d2465e..244ac83ed 100644
--- a/docs/evm-tutorials/evm-mainnet-with-metamask-wallet.md
+++ b/docs/evm-tutorials/evm-mainnet-with-metamask-wallet.md
@@ -1,16 +1,13 @@
---
-title: "EVM Mainnet with Metamask Wallet"
+title: "EVM on Mainnet"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# EVM Mainnet with Metamask Wallet
+# EVM on Mainnet
-This tutorial is for how to set up your Metamask wallet to use with the Mainnet (finney) on Bittensor. You must run this step before you can run other tutorials in this section.
+This page covers how to set up your Metamask wallet to use with the Mainnet (finney) on Bittensor. You must run this step before you can run other tutorials in this section.
-:::tip blog post: EVM on Bittensor
-If you are new to EVM, try this [blog post](https://blog.bittensor.com/evm-on-bittensor-draft-6f323e69aff7) for a simplified explanation.
-:::
Key values:
- The **Bittensor Mainnet URL:** `https://lite.chain.opentensor.ai`
diff --git a/docs/evm-tutorials/evm-on-subtensor.md b/docs/evm-tutorials/evm-on-subtensor.md
deleted file mode 100644
index cfa49fff9..000000000
--- a/docs/evm-tutorials/evm-on-subtensor.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-title: "EVM on Subtensor"
----
-
-import ThemedImage from '@theme/ThemedImage';
-import useBaseUrl from '@docusaurus/useBaseUrl';
-
-# EVM on Subtensor
-
-Ethereum compatibility layer is now available on the subtensor. Using this EVM feature you can:
-- Deploy and interact with any Ethereum smart contract, without any need to change it, on the subtensor blockchain.
-- Access all the standard Ethereum JSON-RPC methods from this EVM compatibility layer on Bittensor.
-
-When this EVM feature is turned ON, it allows the subtensor blockchain to execute Ethereum-compatible smart contracts.
-
-:::danger EVM smart contract executes on subtensor
-Note that all operations performed by the subtensor EVM feature are executed solely on the subtensor blockchain, not on the Ethereum blockchain.
-:::
-
-This document explains in simple terms what this EVM on subtensor is and how it works. Head on over to the [EVM Tutorials](./index.md) to start learning how to use this feature.
-
-## Ethereum vs Bittensor smart contracts
-
-On the Ethereum network, nodes such as full nodes, validator nodes and archive nodes run the Ethereum Virtual Environment (EVM) run-time environment. Smart contracts operate under this EVM. See the below high-level diagram.
-
-When we say “smart contracts on Bittensor” we refer to the new EVM compability feature in the Bittensor subtensor blockchain. When this EVM feature is turned ON, it allows the subtensor blockchain to execute Ethereum-compatible smart contracts. **Note that all operations performed by this new subtensor EVM feature are executed solely on the subtensor blockchain, not on the Ethereum blockchain.** See the below diagram showing how smart contracts on subtensor work:
-
-
-
-
-
-
-
-
-
-
-Next, see [EVM Tutorials](./index.md) to start learning how to use this feature.
diff --git a/docs/evm-tutorials/evm-testnet-with-metamask-wallet.md b/docs/evm-tutorials/evm-testnet-with-metamask-wallet.md
index 17079ed1b..0fac5f4ed 100644
--- a/docs/evm-tutorials/evm-testnet-with-metamask-wallet.md
+++ b/docs/evm-tutorials/evm-testnet-with-metamask-wallet.md
@@ -1,17 +1,16 @@
---
-title: "EVM Testnet with Metamask Wallet"
+title: "EVM on Testnet"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
+import { CreatePartial } from "./_create-mm-wallet.mdx";
-# EVM Testnet with Metamask Wallet
+# EVM on Testnet
-This tutorial is for how to set up your Metamask wallet to use with the testnet on Bittensor. You must run this step before you can run other tutorials in this section.
-:::tip blog post: EVM on Bittensor
-If you are new to EVM, try this [blog post](https://blog.bittensor.com/evm-on-bittensor-draft-6f323e69aff7) for a simplified explanation.
-:::
+This page covers how to set up your Metamask wallet to use with the testnet on Bittensor. You must run this step before you can run other tutorials in this section.
+
Key values:
@@ -19,12 +18,22 @@ Key values:
- **EVM Subtensor Chain ID:** `945` (UTF-8 encoded alpha character)
- **Opentensor EVM-Bittensor GitHub repo:** `https://github.com/opentensor/evm-bittensor/tree/main`
-## Step 1. Create a Metamask wallet
+
-1. If you don't already have it, [install Metamask wallet](https://metamask.io/download/) browser extension.
-2. Create a new account.
+## Connect to EVM Testnet
-### Step 2. Add testnet to Metamask
+Confirm the EVM node is online and accessible. You can check the node status independently using `curl` or similar tools:
+
+```bash
+curl -X POST \
+ -H "Content-Type: application/json" \
+ --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
+ https://test.chain.opentensor.ai
+```
+```console
+{"jsonrpc":"2.0","id":1,"result":"0x460943"}
+```
+### Add testnet to Metamask
Add the testnet to Metamask from within the Metamask wallet. Follow the below steps:
@@ -42,15 +51,11 @@ Add the testnet to Metamask from within the Metamask wallet. Follow the below st
With the above steps, you have successfully configured your Metamask wallet with the testnet.
-## Step 3 Obtain TAO
-
-Next, request testnet TAO in the Bittensor community [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553). Alternatively, you can transfer some testnet TAO to your wallet address using the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
+## Obtain TAO
-## Step 4. Copy Metamask wallet private key into config
+Next, request testnet TAO in the Bittensor community [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553). Alternatively, you can obtain testnet TAO through the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
-:::danger Stop. Did you install the dependencies?
-Before you proceed, make sure you finished the [Install](./install.md) step.
-:::
+## Copy Metamask wallet private key into config
In this step you will copy the private key from your Metamask wallet account and paste it into the configuration file in the repo. This step will ensure that you are not prompted with password each and every step as you run these tutorials.
diff --git a/docs/evm-tutorials/examples.md b/docs/evm-tutorials/examples.md
new file mode 100644
index 000000000..21deed764
--- /dev/null
+++ b/docs/evm-tutorials/examples.md
@@ -0,0 +1,49 @@
+---
+title: "Bittensor EVM: Examples and Precompiles"
+---
+
+import { InstallPartial } from "./_install.mdx";
+
+# Bittensor EVM: Examples and Precompiles
+
+## Available Precompiles
+
+The following precompiled smart contracts are available on the Bittensor EVM.
+The source code can be found [on GitHub](https://github.com/opentensor/subtensor/blob/main/precompiles).
+
+Code examples used throughout are provided by OTF, and come from [this repository.](https://github.com/opentensor/evm-bittensor/tree/main/examples).
+
+## Examples
+
+- [Convert Ethereum (H160) Address to Substrate (SS58)](./convert-h160-to-ss58): Learn how to convert between H160 and SS58 address formats
+- [Converting Between Ethereum and Substrate Addresses](./convert-h160-to-ss58): Learn how to convert between H160 and SS58 address formats
+
+## Standard Ethereum Precompiles
+
+- `ECRecover` (0x1): Recover the address associated with the public key from elliptic curve signature
+- `Sha256` (0x2): SHA-256 hash function
+- `Ripemd160` (0x3): RIPEMD-160 hash function
+- `Identity` (0x4): Identity function (returns input data)
+- `Modexp` (0x5): Modular exponentiation
+- `Sha3FIPS256` (0x400): SHA3-256 hash function (FIPS variant)
+- `ECRecoverPublicKey` (0x401): Recover the public key from an elliptic curve signature
+
+## Bittensor-Specific Precompiles
+
+- [`Ed25519Verify`](./ed25519-verify-precompile.md): Verify Ed25519 signatures
+- [`BalanceTransfer`](./transfer-between-two-h160-accounts.md): Transfer TAO between accounts
+- [`StakingPrecompile`](./staking-precompile.md): Manage staking operations
+- [`StakingPrecompileV2`](./staking-precompile.md) (0x805): Main staking operations including:
+ - `addStake`: Add stake to a hotkey
+ - `removeStake`: Remove stake from a hotkey
+ - `moveStake`: Move stake between hotkeys
+ - `transferStake`: Transfer stake between coldkeys
+ - `getTotalColdkeyStake`: Get total stake for a coldkey
+ - `getTotalHotkeyStake`: Get total stake for a hotkey
+ - `getStake`: Get stake between specific hotkey and coldkey
+ - `addProxy`: Add a proxy delegate
+ - `removeProxy`: Remove a proxy delegate
+- [`SubnetPrecompile`](./subnet-precompile.md): Manage subnet operations
+- [`MetagraphPrecompile`](./metagraph-precompile.md): Interact with the metagraph
+- [`NeuronPrecompile`](./neuron-precompile.md): Manage neuron operations
+
diff --git a/docs/evm-tutorials/hardhat-config-for-subtensor-evm.md b/docs/evm-tutorials/hardhat-config-for-subtensor-evm.md
index 0cca19457..6443faff7 100644
--- a/docs/evm-tutorials/hardhat-config-for-subtensor-evm.md
+++ b/docs/evm-tutorials/hardhat-config-for-subtensor-evm.md
@@ -1,11 +1,11 @@
---
-title: "Hardhat Configuration for Subtensor EVM"
+title: "Configuring Hardhat"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# Hardhat Configuration for Subtensor EVM
+# Configuring Hardhat
You can use [Hardhat](https://hardhat.org/) development environment for the EVM feature on subtensor. The Hardhat networks can be configured using the `hardhat.config.ts` file, as shown below.
diff --git a/docs/evm-tutorials/index.md b/docs/evm-tutorials/index.md
index d11c91272..2ec78378b 100644
--- a/docs/evm-tutorials/index.md
+++ b/docs/evm-tutorials/index.md
@@ -1,5 +1,5 @@
---
-title: "EVM smart contracts on Bittensor"
+title: "Bittensor EVM Smart Contracts"
---
import ThemedImage from '@theme/ThemedImage';
@@ -23,49 +23,72 @@ import { BiSolidNetworkChart } from "react-icons/bi";
import { FaMoneyBillTransfer } from "react-icons/fa6";
import { GrStakeholder } from "react-icons/gr";
-# EVM smart contracts on Bittensor
+# Bittensor EVM Smart Contracts
-Full Ethereum virtual machine (EVM) compatibility is now available on subtensor (the blockchain in Bittensor). This allows users to:
+A full ethereum virtual machine (EVM) runtime operates as an application layer on top of the Bittensor blockchain (Subtensor). This allows users to:
+- deploy most EVM smart contracts on subtensor without changing the code,
+- interact with deployed smart contracts on the subtensor blockchain, and
+- access standard Ethereum JSON-RPC methods.
-- Deploy most EVM smart contracts on subtensor without changing the code
-- Interact with deployed smart contracts on the subtensor blockchain
-- Access standard Ethereum JSON-RPC methods from this EVM compatibility layer on [Subtensor](https://github.com/opentensor/subtensor), Bittensor's substrate blockchain.
+Bittensor EVM smart contracts are executed solely on the **Bittensor blockchain, *not* on the Ethereum blockchain.**
-## Before you proceed
+See:
+- [Examples and Precompiles](./examples.md)
+- [EVM on Testnet](./evm-testnet-with-metamask-wallet)
+- [EVM on Local Chain](./evm-localnet-with-metamask-wallet)
+- [EVM on Mainnet](./evm-mainnet-with-metamask-wallet)
+- [OTF Blogpost: EVM on Bittensor](https://blog.bittensor.com/evm-on-bittensor-draft-6f323e69aff7)
-Before you proceed to use EVM on subtensor, make a note of the following:
+## EVM and Subtensor wallets on the Bittensor blockchian
-1. **EVM smart contract executes on subtensor**: The EVM feature allows the subtensor blockchain to execute Ethereum-compatible smart contracts. Note that all operations performed by this new subtensor EVM feature are executed solely on the subtensor blockchain, not on the Ethereum blockchain.
-2. **1 TAO = 1e18 on subtensor EVM**: While working with the subtensor EVM, 1 TAO should be written as 1 followed by 18 zeroes, i.e., 1e18. See this code example: [https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58).
+Bittensor wallets are based on Polkadot-style ss58 addresses, whereas Ethereum uses h160 addresses.
-Run the below tutorials to learn how to use the EVM feature on the Bittensor blockchain.
+The holder of a private key for an ss58 address based on the corresponding public key can sign transactions on any Bittensor chain for thataddress. Anyone who creates key-pairs using `btcli wallet`, for example, holds the private key and the corresponding seed phrase, and hence can sign Bittensor transactions for that wallet.
-
-
-
-
-
+Similarly, creating an Ethereum wallet gives you control of the h160 private key for the corresponding public key.
+
+It is a simple matter to [convert an h160 address to an ss58 address](./convert-h160-to-ss58), or vice versa, but this does *not* yield the corresponding private key. This means that if you create a wallet in Bittensor, you will not be able to sign Ethereum contracts with it, nor versa.
+
+Hence, in the context of Bittensor EVM we can distinguish between:
+- 'Bittensor wallets': created using the Bittensor tool chain and therefore able to sign transactions using Bittensor transaction clients (BTCLI and the Bittensor SDK), but not EVM smart contracts, on the Bittensor blockchain.
+- 'EVM wallets': created using an EVM client such as MetaMask and therefore able to sign EVM smart contracts, but not Subtensor extrinsics, on the Bittensor blockchain.
+
+
+## Ethereum vs Bittensor EVM smart contract runtime
+
+On the Ethereum network, nodes such as full nodes, validator nodes and archive nodes run the Ethereum Virtual Environment (EVM) run-time environment. Smart contracts operate under this EVM. See the below high-level diagram.
+
+**Note that all operations performed by Bittensor EVM are executed solely on the Bittensor blockchain, not on the Ethereum blockchain.**
+
+
+
+
+
+
+
+
+
+
+
+ body='Get started by installing dependencies first.' />
+
+ {
+ let unsubscribed = false;
+
+ const unsubscribe = call
+ .signAndSend(signer, ({ status, events, dispatchError }) => {
+ const safelyUnsubscribe = () => {
+ if (!unsubscribed) {
+ unsubscribed = true;
+ unsubscribe
+ .then(() => {})
+ .catch((error) => console.error("Failed to unsubscribe:", error));
+ }
+ };
+
+ // Check for transaction errors
+ if (dispatchError) {
+ let errout = dispatchError.toString();
+ if (dispatchError.isModule) {
+ // for module errors, we have the section indexed, lookup
+ const decoded = api.registry.findMetaError(dispatchError.asModule);
+ const { docs, name, section } = decoded;
+ errout = `${name}: ${docs}`;
+ }
+ safelyUnsubscribe();
+ reject(Error(errout));
+ }
+ // Log and resolve when the transaction is included in a block
+ if (status.isInBlock) {
+ safelyUnsubscribe();
+ resolve(status.asInBlock);
+ }
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+}
+
+// for set
+const subnet_contract_abi = [
+ {
+ inputs: [
+ {
+ internalType: "address",
+ name: "initialOwner",
+ type: "address",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint16",
+ name: "netuid",
+ type: "uint16",
+ },
+ ],
+ name: "getHyperParameter",
+ outputs: [
+ {
+ internalType: "uint64",
+ name: "",
+ type: "uint64",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [
+ {
+ internalType: "address",
+ name: "",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "bytes",
+ name: "subnetName",
+ type: "bytes",
+ },
+ {
+ internalType: "bytes",
+ name: "githubRepo",
+ type: "bytes",
+ },
+ {
+ internalType: "bytes",
+ name: "subnetContact",
+ type: "bytes",
+ },
+ ],
+ name: "registerNetwork",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "renounceOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint16",
+ name: "netuid",
+ type: "uint16",
+ },
+ {
+ internalType: "uint64",
+ name: "value",
+ type: "uint64",
+ },
+ ],
+ name: "setHyperParameter",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+];
+
+// compile with evm version 0.8.3
+const subnet_contract_bytecode =
+ "0x608060405234801561001057600080fd5b50604051610e6d380380610e6d8339818101604052810190610032919061015c565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561006d57600080fd5b61007c8161008360201b60201c565b50506101ce565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600081519050610156816101b7565b92915050565b60006020828403121561016e57600080fd5b600061017c84828501610147565b91505092915050565b600061019082610197565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6101c081610185565b81146101cb57600080fd5b50565b610c90806101dd6000396000f3fe6080604052600436106100555760003560e01c8063290212c11461005a578063715018a614610076578063786fede51461008d57806378b63cb6146100ca5780638da5cb5b146100e6578063f2fde38b14610111575b600080fd5b610074600480360381019061006f919061077b565b61013a565b005b34801561008257600080fd5b5061008b610279565b005b34801561009957600080fd5b506100b460048036038101906100af9190610812565b61028d565b6040516100c19190610a3c565b60405180910390f35b6100e460048036038101906100df919061083b565b6103df565b005b3480156100f257600080fd5b506100fb61051a565b6040516101089190610971565b60405180910390f35b34801561011d57600080fd5b5061013860048036038101906101339190610752565b610543565b005b610142610591565b60006108039050600061080373ffffffffffffffffffffffffffffffffffffffff163463290212c160e01b8787876040516024016101829392919061098c565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101ec919061095a565b60006040518083038185875af1925050503d8060008114610229576040519150601f19603f3d011682016040523d82523d6000602084013e61022e565b606091505b5050905080610272576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610269906109d8565b60405180910390fd5b5050505050565b610281610591565b61028b60006105d2565b565b600080610803905060008061080373ffffffffffffffffffffffffffffffffffffffff16637444dadc60e01b866040516024016102ca91906109f8565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610334919061095a565b6000604051808303816000865af19150503d8060008114610371576040519150601f19603f3d011682016040523d82523d6000602084013e610376565b606091505b5091509150816103bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b2906109d8565b60405180910390fd5b6000818060200190518101906103d19190610877565b9050809450505050919050565b6103e7610591565b60006108039050600061080373ffffffffffffffffffffffffffffffffffffffff1663b38e0bbe60e01b8585604051602401610424929190610a13565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161048e919061095a565b6000604051808303816000865af19150503d80600081146104cb576040519150601f19603f3d011682016040523d82523d6000602084013e6104d0565b606091505b5050905080610514576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161050b906109d8565b60405180910390fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b61054b610591565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561058557600080fd5b61058e816105d2565b50565b3373ffffffffffffffffffffffffffffffffffffffff166105b061051a56b73ffffffffffffffffffffffffffffffffffffffff16146105d057600080fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60006106a96106a484610a7c565b610a57565b9050828152602081018484840111156106c157600080fd5b6106cc848285610b39565b509392505050565b6000813590506106e381610c15565b92915050565b600082601f8301126106fa57600080fd5b813561070a848260208601610696565b91505092915050565b60008135905061072281610c2c565b92915050565b60008135905061073781610c43565b92915050565b60008151905061074c81610c43565b92915050565b60006020828403121561076457600080fd5b6000610772848285016106d4565b91505092915050565b60008060006060848603121561079057600080fd5b600084013567ffffffffffffffff8111156107aa57600080fd5b6107b6868287016106e9565b935050602084013567ffffffffffffffff8111156107d357600080fd5b6107df868287016106e9565b925050604084013567ffffffffffffffff8111156107fc57600080fd5b610808868287016106e9565b9150509250925092565b60006020828403121561082457600080fd5b600061083284828501610713565b91505092915050565b6000806040838503121561084e57600080fd5b600061085c85828601610713565b925050602061086d85828601610728565b9150509250929050565b60006020828403121561088957600080fd5b60006108978482850161073d565b91505092915050565b6108a981610ae5565b82525050565b60006108ba82610aad565b6108c48185610ab8565b93506108d4818560208601610b48565b6108dd81610bdb565b840191505092915050565b60006108f382610aad565b6108fd8185610ac9565b935061090d818560208601610b48565b80840191505092915050565b6000610926601283610ad4565b915061093182610bec565b602082019050919050565b61094581610af7565b82525050565b61095481610b25565b82525050565b600061096682846108e8565b915081905092915050565b600060208201905061098660008301846108a0565b92915050565b600060608201905081810360008301526109a681866108af565b905081810360208301526109ba81856108af565b905081810360408301526109ce81846108af565b9050949350505050565b60006020820190506109f181610919565b9050919050565b6000602082019050610a0d600083018461093c565b92915050565b6000604082019050610a28600083018561093c565b610a35602083018461094b565b9392505050565b6000602082019050610a51600083018461094b565b92915050565b6000610a61610a72565b9050610a6d8282610b7b565b919050565b6000604051905090565b600067ffffffffffffffff821115610a9757610a96610bac565b5b610aa082610bdb565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b6000610af082610b05565b9050919050565b600061ffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600067ffffffffffffffff82169050919050565b82818337600083830152505050565b60005b83811015610b66578082015181840152602081019050610b4b565b83811115610b75576000848401525b50505050565b610b8482610bdb565b810181811067ffffffffffffffff82111715610ba357610ba2610bac565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b7f5375626e65742063616c6c206661696c65640000000000000000000000000000600082015250565b610c1e81610ae5565b8114610c2957600080fd5b50565b610c3581610af7565b8114610c4057600080fd5b50565b610c4c81610b25565b8114610c5757600080fd5b5056fea26469706673582212200e657685be0d4a155c28ec7471273753d1c625c562f268b2efdf0a8b2c7e4dbe64736f6c63430008030033";
+
+// Create a signer
+const privateKey = ethPrivateKey; // DO NOT HARDCODE YOUR PRIVATE KEY IN PRODUCTION
+const signer = new ethers.Wallet(privateKey, provider);
+
+async function createSubnetGetSetParameter() {
+ try {
+ // Substrate ss58 address that will receive the transfer
+ const wsProvider = new WsProvider(wsUrl);
+ const api = await ApiPromise.create({ provider: wsProvider });
+ const keyring = new Keyring({ type: "sr25519" });
+ const account = keyring.addFromUri(subSeed); // Your Substrate address private key/seed
+
+ // Destination address can be replaced with any ss58 address here:
+ const destinationAddress = account.address;
+
+ // Get the substrate address public key
+ const pubk = decodeAddress(destinationAddress);
+ const hex = Array.from(pubk, (byte) =>
+ byte.toString(16).padStart(2, "0")
+ ).join("");
+
+ const signer = new ethers.Wallet(ethPrivateKey, provider);
+
+ const ss58mirror = convertH160ToSS58(signer.address);
+ let txSudoSetBalance = api.tx.sudo.sudo(
+ api.tx.balances.forceSetBalance(ss58mirror, BigInt(1e18).toString())
+ );
+ await sendTransaction(api, txSudoSetBalance, account);
+
+ const txSudoSetWhitelist = api.tx.sudo.sudo(
+ api.tx.evm.setWhitelist([signer.address])
+ );
+
+ await sendTransaction(api, txSudoSetWhitelist, account);
+
+ const contractFactory = new ethers.ContractFactory(
+ subnet_contract_abi,
+ subnet_contract_bytecode,
+ signer
+ );
+
+ const subnet_contract = await contractFactory.deploy(signer.address);
+ await subnet_contract.waitForDeployment();
+
+ console.log("deployed contract address: ", subnet_contract.target);
+
+ txSudoSetBalance = api.tx.sudo.sudo(
+ api.tx.balances.forceSetBalance(
+ convertH160ToSS58(subnet_contract.target),
+ BigInt(1e16).toString()
+ )
+ );
+ await sendTransaction(api, txSudoSetBalance, account);
+
+ let totalNetwork = Number(await api.query.subtensorModule.totalNetworks());
+ console.log("total networks is ", totalNetwork);
+
+ // there are predefined network 0 and 3.
+ let netuid;
+ if (totalNetwork > 3) {
+ netuid = totalNetwork;
+ } else {
+ netuid = totalNetwork - 1;
+ }
+
+ const encoder = new TextEncoder();
+
+ let tx = await subnet_contract.registerNetwork(
+ encoder.encode("name"),
+ encoder.encode("repo"),
+ encoder.encode("contact")
+ );
+ await tx.wait();
+
+ // the network owner is the deployed contract, not the signer
+ const networkOwner = (
+ await api.query.subtensorModule.subnetOwner(netuid)
+ ).toHuman();
+ console.log("networkOwner is ", networkOwner);
+
+ // Note: This example uses setHyperParameter which calls setServingRateLimit
+ // Some other functions like setMinBurn, setMaxBurn, setWeightsSetRateLimit are deprecated
+ tx = await subnet_contract.setHyperParameter(netuid, 255);
+ await tx.wait();
+
+ // get parameter from chain
+ let parameter = Number(
+ await api.query.subtensorModule.servingRateLimit(netuid)
+ );
+
+ assert(parameter == 255);
+
+ // get paramter from contract
+ parameter = await subnet_contract.getHyperParameter(netuid);
+
+ // check total networks after registration
+ console.log(
+ "total networks is ",
+ (await api.query.subtensorModule.totalNetworks()).toHuman()
+ );
+
+ process.exit(0);
+ } catch (error) {
+ console.error("Error:", error);
+ process.exit(0);
+ }
+}
+
+async function main() {
+ await createSubnetGetSetParameter();
+}
+
+main().catch(console.error);
+
+```
+### Solidity
+```sol
+// SPDX-License-Identifier: GPL-3.0
+//
+// This example demonstrates calling of ISubnet precompile
+// from another smart contract
+
+pragma solidity ^0.8.3;
+import "@openzeppelin/contracts/access/Ownable.sol";
+
+address constant ISUBTENSOR_SUBNET_ADDRESS = 0x0000000000000000000000000000000000000803;
+
+interface ISubnet {
+ /// Registers a new network without specifying details.
+ // function registerNetwork() external payable;
+ /// Registers a new network with specified subnet name, GitHub repository, and contact information.
+ function registerNetwork(
+ bytes memory subnetName,
+ bytes memory githubRepo,
+ bytes memory subnetContact
+ ) external payable;
+
+ function getServingRateLimit(uint16 netuid) external view returns (uint64);
+
+ function setServingRateLimit(
+ uint16 netuid,
+ uint64 servingRateLimit
+ ) external payable;
+}
+
+contract Subnet is Ownable {
+ constructor(address initialOwner) Ownable(initialOwner) {}
+
+ function registerNetwork(
+ bytes memory subnetName,
+ bytes memory githubRepo,
+ bytes memory subnetContact
+ ) external payable onlyOwner {
+ ISubnet subnetPrecompile = ISubnet(ISUBTENSOR_SUBNET_ADDRESS);
+ (bool success, ) = ISUBTENSOR_SUBNET_ADDRESS.call{value: msg.value}(
+ abi.encodeWithSelector(
+ subnetPrecompile.registerNetwork.selector,
+ subnetName,
+ githubRepo,
+ subnetContact
+ )
+ );
+ require(success, "Subnet call failed");
+ }
+
+ function setHyperParameter(
+ uint16 netuid,
+ uint64 value
+ ) external payable onlyOwner {
+ ISubnet subnetPrecompile = ISubnet(ISUBTENSOR_SUBNET_ADDRESS);
+ (bool success, ) = ISUBTENSOR_SUBNET_ADDRESS.call(
+ abi.encodeWithSelector(
+ subnetPrecompile.setServingRateLimit.selector,
+ netuid,
+ value
+ )
+ );
+ require(success, "Subnet call failed");
+ }
+
+ function getHyperParameter(uint16 netuid) public returns (uint64) {
+ ISubnet subnetPrecompile = ISubnet(ISUBTENSOR_SUBNET_ADDRESS);
+ (bool success, bytes memory data) = ISUBTENSOR_SUBNET_ADDRESS.call(
+ abi.encodeWithSelector(
+ subnetPrecompile.getServingRateLimit.selector,
+ netuid
+ )
+ );
+ require(success, "Subnet call failed");
+
+ uint64 value = abi.decode(data, (uint64));
+ return value;
+ }
+}
+
+```
diff --git a/docs/evm-tutorials/subtensor-networks.md b/docs/evm-tutorials/subtensor-networks.md
index b9947cb97..6178ef3d8 100644
--- a/docs/evm-tutorials/subtensor-networks.md
+++ b/docs/evm-tutorials/subtensor-networks.md
@@ -1,11 +1,11 @@
---
-title: "Subtensor Networks"
+title: "EVM Network Details"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# Subtensor Networks
+# EVM Network Details
| DESCRIPTION | MAINNET | TESTNET | LOCALNET |
|:---------------------|:------------------------------------|:-------------------------------------|:-------------------------|
diff --git a/docs/evm-tutorials/transfer-between-two-h160-accounts.md b/docs/evm-tutorials/transfer-between-two-h160-accounts.md
index 9a1c3151c..87eec58d3 100644
--- a/docs/evm-tutorials/transfer-between-two-h160-accounts.md
+++ b/docs/evm-tutorials/transfer-between-two-h160-accounts.md
@@ -31,7 +31,11 @@ You must run either [EVM Localnet with Metamask Wallet](./evm-localnet-with-meta
3. Configure the amount to be sent. In this example we are using large numbers so that the result is visible in Metamask: Because Metamask doesn't respect decimals of 9 and always defaults to 18 decimals. In production environment 0.1 TAO will match to "100000000000" (10^8), while for this demonstration we have to use "100000000000000000" (10^17), which will appear as "0.1 TAO" in Metamask, but will actually be equal to 100000000 TAO (10^8 TAO).
:::tip 1 TAO = 1e18 on subtensor EVM
- While working with the subtensor EVM, 1 TAO should be written as 1 followed by 18 zeroes, i.e., 1e18. Also see this code example: [https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58).
+
+ In Bittensor EVM, 1 TAO should be written as $1e18$
+
+ For [example](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58): `const value = BigInt(0.5 * 1e18).toString();`
+
:::
```js
diff --git a/docs/evm-tutorials/transfer-from-metamask-to-ss58.md b/docs/evm-tutorials/transfer-from-metamask-to-ss58.md
index 6ff2464db..f7dafb785 100644
--- a/docs/evm-tutorials/transfer-from-metamask-to-ss58.md
+++ b/docs/evm-tutorials/transfer-from-metamask-to-ss58.md
@@ -1,135 +1,142 @@
---
-title: "Transfer from Metamask to SS58 address"
+title: "Transfer TAO from Metamask to SS58 Address"
---
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-# Transfer from Metamask to SS58 address
+# Transfer TAO from Metamask to SS58 Address
-In this tutorial you will learn how to transfer TAO from your Metamask wallet to your Bittensor SS58 address for a coldkey (wallet) or a hotkey. You will learn how to do this via two different methods:
+In this tutorial you will learn how to transfer TAO from your Metamask wallet to your Bittensor SS58 address for a coldkey (wallet) or a hotkey. There are two different options:
-- **Method 1:** Transfer using a precompiled contract.
-- **Method 2:** Transfer using the `withdraw` extrinsic in the `evm` pallet in subtensor blockchain.
+- [**Option 1:** Transfer using a precompiled contract](#option-1-transfer-using-a-precompiled-contract).
+- [**Option 2:** Transfer using the `withdraw` extrinsic in the `evm` pallet in subtensor blockchain](#option-2-transfer-using-the-withdraw-extrinsic-in-the-subtensor-evm-pallet).
-## Prerequisite
+## Prerequisites
-:::danger stop, did you set up your Metamask wallet for EVM?
-You must run [EVM Testnet with Metamask Wallet](./evm-testnet-with-metamask-wallet.md) tutorial before you can run this tutorial.
-:::
+- **Node.js** (v16 or later recommended)
+- **npm** or **yarn**
+- [Clone the Bittensor EVM examples repo](./install.md)
+- [Get set up for using EVM wallet on testnet](./evm-testnet-with-metamask-wallet)
+- [Install](./install) the EVM-Bittensor repo, containing scripts and examples.
+
+
+## Option 1: Transfer using a Precompiled Contract
+
+**Best for:** Most users. You do NOT need the private key or seed for your SS58 address.
-## Method 1: Transfer using a precompiled contract
+This option uses a precompiled contract to transfer TAO from your Metamask wallet to any SS58 address (coldkey or hotkey).
-The private key or the seed for your SS58 is **not required** for this method.
+### Configure the destination address
-This step will transfer 0.5 TAO to your `ss58` destination address specified in the [`withdraw.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js) file. Look for the following lines in this file:
+Open [`withdraw.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js) in the EVM-Bittensor repo. Find the following line:
-```javascript
+```js
// Destination address can be replaced with any ss58 address here:
const destinationAddress = account.address;
```
-and provide your `ss58` destination address as shown below:
+Replace it with your own SS58 address:
-```javascript
+```js
const destinationAddress = "5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y";
```
-:::danger Stop. Did you install the dependencies?
-Before you proceed, make sure you finished the [Install](./install.md) step.
-:::
-
-Next, navigate to the `examples` directory of the EVM-Bittensor repo:
- ```bash
- cd examples
- ```
-Run:
+### Run the transfer script
```bash
node transfer.js
```
-:::tip 1 TAO = 1e18 on subtensor EVM
-While working with the subtensor EVM, 1 TAO should be written as 1 followed by 18 zeroes, i.e., 1e18. See this code example: [https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58).
+:::tip 1 TAO = 1e18
+In Bittensor EVM, 1 TAO is written as `1e18` (just like on Ethereum). For example, to send 0.5 TAO:
+```js
+const value = BigInt(0.5 * 1e18).toString();
+```
:::
-Then, run:
+### Run the withdraw script
```bash
node withdraw.js
```
-You will see the output similar to below, indicating a successful transfer of TAO from your Metamask account to your `ss58` destination address:
+You should see output similar to:
```bash showLineNumbers
node withdraw.js
2024-10-07 15:34:58 REGISTRY: Unknown signed extensions SubtensorSignedExtension, CommitmentsSignedExtension found, treating them as no-effect
-2024-10-07 15:34:58 API/INIT: RPC methods not decorated: chainHead_v1_body, chainHead_v1_call, chainHead_v1_continue, chainHead_v1_follow, chainHead_v1_header, chainHead_v1_stopOperation, chainHead_v1_storage, chainHead_v1_unfollow, chainHead_v1_unpin, chainSpec_v1_chainName, chainSpec_v1_genesisHash, chainSpec_v1_properties, debug_getBadBlocks, debug_getRawBlock, debug_getRawHeader, debug_getRawReceipts, debug_getRawTransaction, delegateInfo_getDelegate, delegateInfo_getDelegated, delegateInfo_getDelegates, eth_getBlockReceipts, neuronInfo_getNeuron, neuronInfo_getNeuronLite, neuronInfo_getNeurons, neuronInfo_getNeuronsLite, subnetInfo_getLockCost, subnetInfo_getSubnetHyperparams, subnetInfo_getSubnetInfo, subnetInfo_getSubnetInfo_v2, subnetInfo_getSubnetsInf_v2, subnetInfo_getSubnetsInfo, transactionWatch_v1_submitAndWatch, transactionWatch_v1_unwatch, transaction_v1_broadcast, transaction_v1_stop
-2024-10-07 15:34:58 API/INIT: node-subtensor/302: Not decorating unknown runtime apis: 0x42e62be4a39e5b60/1, 0x806df4ccaa9ed485/1, 0x8375104b299b74c5/1, 0x5d1fbfbe852f2807/1, 0xc6886e2f8e598b0a/1
+...
Sending balance to ss58 address: 5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y
pubk = f873b72b75b9029397edceaa04cf08cc97909c8b6304f2ccc3593641bf92e97c
-Transaction response: ContractTransactionResponse {
- provider: JsonRpcProvider {},
- blockNumber: null,
- blockHash: null,
- index: undefined,
- hash: '0x4f3bde9e678d7307f2c07dd3212d6920db8e2af8ade052a823b3ad1f28ddc221',
- type: 2,
- to: '0x0000000000000000000000000000000000000800',
- from: '0x709615c655B24919F48B365D292521EFcC74467B',
- nonce: 0,
- gasLimit: 21576n,
- gasPrice: undefined,
- maxPriorityFeePerGas: 0n,
- maxFeePerGas: 20000000000n,
- maxFeePerBlobGas: null,
- data: '0xcd6f4eb1f873b72b75b9029397edceaa04cf08cc97909c8b6304f2ccc3593641bf92e97c',
- value: 500000000000000000n,
- chainId: 945n,
- signature: Signature { r: "0xc8cf1d54513eb26ee13ca8e001201e918d50593ce6efd4ceee6645ec1879f183", s: "0x6594fe686ecac6131b536b9ff5277f40da1d12ab6c2a269693029c58cef8417d", yParity: 0, networkV: null },
- accessList: [],
- blobVersionedHashes: null
-}
+Transaction response: ContractTransactionResponse { ... }
Transaction confirmed.
```
-In the above example, a coldkey `ss58` address `5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y` (line 5 in the above log) is used as a destination address. The Metamask wallet address used is: `0x709615c655B24919F48B365D292521EFcC74467B` (line 15 in the above log).
+:::info
+The `ss58` address is your destination (coldkey or hotkey). The `from` address is your Metamask wallet.
+:::
+
+### Check your SS58 balance
-Finally, use the below `btcli` command to check the balance of your `ss58` address (the below `--ss58` option is supported in BTCLI 8.2.0 or later versions):
+Use the Bittensor CLI to check your new balance:
```bash
btcli wallet balance --ss58 5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y
```
-## Method 2: Transfer using `withdraw` extrinsic in subtensor `evm` pallet
+## Option 2: Transfer using the `withdraw` Extrinsic in the Subtensor EVM Pallet
+
+**Best for:** Advanced users. You WILL need the private key for your SS58 address.
+
+This option uses the `withdraw` extrinsic in the EVM pallet, allowing you to transfer from an EVM address to an SS58 address using Polkadot.js Apps.
+
+### Copy your SS58 address
+
+For example: `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`. You will need the private key for this address set up in the Polkadot.js extension.
+
+### Edit the destination in the script
+
+Open [`withdraw-address.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw-address.js) and set your SS58 address:
+
+```js
+const ss58Address = "";
+```
+
+### Run the withdraw-address script
+
+```bash
+node withdraw-address.js
+```
+
+### Copy the "Ethereum mirror" output address
+
+The script will output an "Ethereum mirror" address. Copy this address.
+
+### Transfer TAO to the mirror address using Metamask
+
+- Open Metamask and send the desired amount of TAO to the mirror address.
+- If you restarted the network, clear Metamask's activity tab data: **Settings > Advanced > Clear activity tab data**.
-You will need the private key for your SS58 for this method.
+### Ensure your destination address is funded
-1. Copy your `ss58` address (for example: `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`). You need the private key for this address setup in Polkadot JS extension.
-2. Paste it into `ss58Address` in main function in [`withdraw-address.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw-address.js) script.
+Make sure the destination address has enough TAO to pay for transaction fees.
-3. Next, navigate to the `examples` directory of the EVM-Bittensor repo:
+### Open the Extrinsics page in Polkadot.js Apps
- ```bash
- cd examples
- ```
+[Polkadot.js Apps Extrinsics](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftest.chain.opentensor.ai%3A443#/extrinsics)
-4. Run:
+### Submit the withdraw extrinsic
- ```bash
- node withdraw-address.js
- ```
+- Select the `evm` pallet and `withdraw` extrinsic.
+- Paste the "Ethereum mirror" address into the address field.
+- Enter the amount you are transferring.
+- Note: Metamask balances are by 10^9 lower than Polkadot Apps UI balances (Metamask does not respect 10^9 decimals for native currency).
-5. Copy the "Ethereum mirror:" output address.
-6. Transfer the amount to this address that you wish to transfer using Metamask. Make sure to clear activity tab data if you restarted the network previously: **Settings** > **Advanced** > **Clear activity tab data**.
-7. Make sure your destination address is funded to run a transaction.
-8. Open the **Extrisics** section in Polkadot JS app: [https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftest.chain.opentensor.ai%3A443#/extrinsics](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftest.chain.opentensor.ai%3A443#/extrinsics).
-9. Select `evm` pallet and `withdraw` extrinsic.
-10. Paste the "Ethereum mirror" output address into address field.
-11. Put the amount you are transferring into amount field. Note that Metamask balances are by 10^9 lower than Polkadot Apps UI balances because Metamask will not respect 10^9 decimals for native currency before we have a corresponding PR to https://github.com/ethereum-lists merged.
-12. Submit the transaction.
-13. Finally, use the below `btcli` command to check the balance of your `ss58` address (the below `--ss58` option is supported in BTCLI 8.2.0 or later versions):
+### Submit the transaction
- ```bash
- btcli wallet balance --ss58
- ```
+### Check your SS58 balance
+
+```bash
+btcli wallet balance --ss58
+```
diff --git a/docs/evm-tutorials/transfer-from-metamask-to-ss58EDGE.md b/docs/evm-tutorials/transfer-from-metamask-to-ss58EDGE.md
new file mode 100644
index 000000000..1774226b9
--- /dev/null
+++ b/docs/evm-tutorials/transfer-from-metamask-to-ss58EDGE.md
@@ -0,0 +1,128 @@
+---
+title: "Transfer from Metamask to SS58 address"
+---
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+# Transfer from Metamask to SS58 address
+
+In this tutorial you will learn how to transfer TAO from your Metamask wallet to your Bittensor SS58 address for a coldkey (wallet) or a hotkey. You will learn how to do this via two different methods:
+
+- **Method 1:** Transfer using a precompiled contract.
+- **Method 2:** Transfer using the `withdraw` extrinsic in the `evm` pallet in subtensor blockchain.
+
+## Prerequisite
+
+:::danger stop, did you set up your Metamask wallet for EVM?
+You must run [EVM Testnet with Metamask Wallet](./evm-testnet-with-metamask-wallet.md) tutorial before you can run this tutorial.
+:::
+
+## Method 1: Transfer using a precompiled contract
+
+The private key or the seed for your SS58 is **not required** for this method.
+
+This step will transfer 0.5 TAO to your `ss58` destination address specified in the [`withdraw.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js) file. Look for the following lines in this file:
+
+```javascript
+// Destination address can be replaced with any ss58 address here:
+const destinationAddress = account.address;
+```
+
+and provide your `ss58` destination address as shown below:
+
+```javascript
+const destinationAddress = "5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y";
+```
+
+Run:
+
+```bash
+node transfer.js
+```
+
+:::tip
+In Bittensor EVM, 1 TAO should be written as $1e18$
+
+For [example](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw.js#L58): `const value = BigInt(0.5 * 1e18).toString();`
+:::
+
+Then, run:
+
+```bash
+node withdraw.js
+```
+
+You will see the output similar to below, indicating a successful transfer of TAO from your Metamask account to your `ss58` destination address:
+
+```bash showLineNumbers
+node withdraw.js
+2024-10-07 15:34:58 REGISTRY: Unknown signed extensions SubtensorSignedExtension, CommitmentsSignedExtension found, treating them as no-effect
+2024-10-07 15:34:58 API/INIT: RPC methods not decorated: chainHead_v1_body, chainHead_v1_call, chainHead_v1_continue, chainHead_v1_follow, chainHead_v1_header, chainHead_v1_stopOperation, chainHead_v1_storage, chainHead_v1_unfollow, chainHead_v1_unpin, chainSpec_v1_chainName, chainSpec_v1_genesisHash, chainSpec_v1_properties, debug_getBadBlocks, debug_getRawBlock, debug_getRawHeader, debug_getRawReceipts, debug_getRawTransaction, delegateInfo_getDelegate, delegateInfo_getDelegated, delegateInfo_getDelegates, eth_getBlockReceipts, neuronInfo_getNeuron, neuronInfo_getNeuronLite, neuronInfo_getNeurons, neuronInfo_getNeuronsLite, subnetInfo_getLockCost, subnetInfo_getSubnetHyperparams, subnetInfo_getSubnetInfo, subnetInfo_getSubnetInfo_v2, subnetInfo_getSubnetsInf_v2, subnetInfo_getSubnetsInfo, transactionWatch_v1_submitAndWatch, transactionWatch_v1_unwatch, transaction_v1_broadcast, transaction_v1_stop
+2024-10-07 15:34:58 API/INIT: node-subtensor/302: Not decorating unknown runtime apis: 0x42e62be4a39e5b60/1, 0x806df4ccaa9ed485/1, 0x8375104b299b74c5/1, 0x5d1fbfbe852f2807/1, 0xc6886e2f8e598b0a/1
+Sending balance to ss58 address: 5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y
+pubk = f873b72b75b9029397edceaa04cf08cc97909c8b6304f2ccc3593641bf92e97c
+Transaction response: ContractTransactionResponse {
+ provider: JsonRpcProvider {},
+ blockNumber: null,
+ blockHash: null,
+ index: undefined,
+ hash: '0x4f3bde9e678d7307f2c07dd3212d6920db8e2af8ade052a823b3ad1f28ddc221',
+ type: 2,
+ to: '0x0000000000000000000000000000000000000800',
+ from: '0x709615c655B24919F48B365D292521EFcC74467B',
+ nonce: 0,
+ gasLimit: 21576n,
+ gasPrice: undefined,
+ maxPriorityFeePerGas: 0n,
+ maxFeePerGas: 20000000000n,
+ maxFeePerBlobGas: null,
+ data: '0xcd6f4eb1f873b72b75b9029397edceaa04cf08cc97909c8b6304f2ccc3593641bf92e97c',
+ value: 500000000000000000n,
+ chainId: 945n,
+ signature: Signature { r: "0xc8cf1d54513eb26ee13ca8e001201e918d50593ce6efd4ceee6645ec1879f183", s: "0x6594fe686ecac6131b536b9ff5277f40da1d12ab6c2a269693029c58cef8417d", yParity: 0, networkV: null },
+ accessList: [],
+ blobVersionedHashes: null
+}
+Transaction confirmed.
+```
+
+In the above example, a coldkey `ss58` address `5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y` (line 5 in the above log) is used as a destination address. The Metamask wallet address used is: `0x709615c655B24919F48B365D292521EFcC74467B` (line 15 in the above log).
+
+Finally, use the below `btcli` command to check the balance of your `ss58` address (the below `--ss58` option is supported in BTCLI 8.2.0 or later versions):
+
+```bash
+btcli wallet balance --ss58 5HgU7B3xfSfisR1A7wDMt7FHX5Uizj6xtWWHwhwJMZSrdN7y
+```
+
+## Method 2: Transfer using `withdraw` extrinsic in subtensor `evm` pallet
+
+You will need the private key for your SS58 for this method.
+
+1. Copy your `ss58` address (for example: `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`). You need the private key for this address setup in Polkadot JS extension.
+2. Paste it into `ss58Address` in main function in [`withdraw-address.js`](https://github.com/opentensor/evm-bittensor/blob/main/examples/withdraw-address.js) script.
+
+3. Next, navigate to the `examples` directory of the EVM-Bittensor repo:
+
+ ```bash
+ cd examples
+ ```
+
+4. Run:
+
+ ```bash
+ node withdraw-address.js
+ ```
+
+5. Copy the "Ethereum mirror:" output address.
+6. Transfer the amount to this address that you wish to transfer using Metamask. Make sure to clear activity tab data if you restarted the network previously: **Settings** > **Advanced** > **Clear activity tab data**.
+7. Make sure your destination address is funded to run a transaction.
+8. Open the **Extrisics** section in Polkadot JS app: [https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftest.chain.opentensor.ai%3A443#/extrinsics](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftest.chain.opentensor.ai%3A443#/extrinsics).
+9. Select `evm` pallet and `withdraw` extrinsic.
+10. Paste the "Ethereum mirror" output address into address field.
+11. Put the amount you are transferring into amount field. Note that Metamask balances are by 10^9 lower than Polkadot Apps UI balances because Metamask will not respect 10^9 decimals for native currency before we have a corresponding PR to https://github.com/ethereum-lists merged.
+12. Submit the transaction.
+13. Finally, use the below `btcli` command to check the balance of your `ss58` address (the below `--ss58` option is supported in BTCLI 8.2.0 or later versions):
+
+ ```bash
+ btcli wallet balance --ss58
+ ```
diff --git a/docs/evm-tutorials/withdraw-from-alice.md b/docs/evm-tutorials/withdraw-from-alice.md
new file mode 100644
index 000000000..d2cd15ce9
--- /dev/null
+++ b/docs/evm-tutorials/withdraw-from-alice.md
@@ -0,0 +1,138 @@
+---
+title: "Withdraw TAO from Alice Account (Local Development)"
+---
+
+import { InstallPartial } from "./_install.mdx";
+import { CreatePartial } from "./_create-mm-wallet.mdx";
+
+# Withdraw TAO from Alice Account (Local Development)
+
+Every locally deployed dev-mode blockchain comes provisioned with an 'Alice' account holding a large bag of TAO.
+
+This page shows how to withdraw TAO to your wallet, using a transaction that requires root permissions, and therefore is only available in local development.
+
+## Prerequesites
+
+[Deploy a Subtensor Blockchain locally](../local-build/deploy)
+
+
+## Procedure
+
+1.
+
+1.
+
+### Configure your request
+
+The withdraw.js script expects your configuration to be available in config.js.
+Select the local configuration options for `rpcURL` and `wsUrl`.
+
+:::danger
+Handle your private keys with care. Do not commit them to Github.
+:::
+```
+// PROTECT YOUR PRIVATE KEYS WELL, NEVER COMMIT THEM TO GITHUB OR SHARE WITH ANYONE
+const ethPrivateKey = ;
+const subSeed = "//Alice";
+const rpcUrlLocal = 'http://127.0.0.1:9946';
+const rpcUrlTestnet = 'https://test.chain.opentensor.ai';
+const wsUrlLocal = 'ws://127.0.0.1:9946';
+const wsUrlTestnet = 'wss://test.chain.opentensor.ai';
+
+module.exports = {
+ ethPrivateKey,
+ subSeed,
+ rpcUrl: rpcUrlLocal,
+ wsUrl: wsUrlLocal,
+}
+```
+### Run the script
+
+```bash
+node withdraw.js
+```
+
+**Source code**:
+
+- [EVM examples repo](https://github.com/opentensor/evm-bittensor)
+
+```javascript
+const { ethers } = require('ethers');
+const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api');
+const { convertH160ToSS58 } = require('./address-mapping.js');
+
+// PROTECT YOUR PRIVATE KEYS WELL, NEVER COMMIT THEM TO GITHUB OR SHARE WITH ANYONE
+const { ethPrivateKey, subSeed, rpcUrl, wsUrl } = require('./config.js');
+
+function sendTransaction(api, call, signer) {
+ return new Promise((resolve, reject) => {
+ let unsubscribed = false;
+
+ const unsubscribe = call.signAndSend(signer, ({ status, events, dispatchError }) => {
+ const safelyUnsubscribe = () => {
+ if (!unsubscribed) {
+ unsubscribed = true;
+ unsubscribe.then(() => {})
+ .catch(error => console.error('Failed to unsubscribe:', error));
+ }
+ };
+
+ // Check for transaction errors
+ if (dispatchError) {
+ let errout = dispatchError.toString();
+ if (dispatchError.isModule) {
+ // for module errors, we have the section indexed, lookup
+ const decoded = api.registry.findMetaError(dispatchError.asModule);
+ const { docs, name, section } = decoded;
+ errout = `${name}: ${docs}`;
+ }
+ safelyUnsubscribe();
+ reject(Error(errout));
+ }
+ // Log and resolve when the transaction is included in a block
+ if (status.isInBlock) {
+ safelyUnsubscribe();
+ resolve(status.asInBlock);
+ }
+ }).catch((error) => {
+ reject(error);
+ });
+ });
+}
+
+async function main() {
+ const wsProvider = new WsProvider(wsUrl);
+ const api = await ApiPromise.create({ provider: wsProvider });
+ const keyring = new Keyring({ type: 'sr25519' });
+
+ const sender = keyring.addFromUri(subSeed); // Your sender's private key/seed
+
+ // Get ethereum address that matches the private key from the secrets file
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
+ const signer = new ethers.Wallet(ethPrivateKey, provider);
+ const recipientEthereumAddress = signer.address;
+
+ const ss58Address = convertH160ToSS58(recipientEthereumAddress);
+ console.log(`Mirror: ${ss58Address}`);
+ // Amount to send: 1 TAO on Substrate side = 1*10^9
+ const amount = "1000000000";
+
+ // Alice funds herself with 1M TAO
+ const txSudoSetBalance = api.tx.sudo.sudo(
+ api.tx.balances.forceSetBalance(sender.address, "1000000000000000")
+ );
+ await sendTransaction(api, txSudoSetBalance, sender);
+ console.log('Balace force-set');
+
+ // Create a transfer transaction
+ const transfer = api.tx.balances.transferKeepAlive(ss58Address, amount);
+
+ // Sign and send the transaction
+ await sendTransaction(api, transfer, sender);
+ console.log(`Transfer sent to ${recipientEthereumAddress} (its ss58 mirror address is: ${ss58Address})`);
+ await api.disconnect();
+}
+
+main().catch(console.error);
+
+```
diff --git a/docs/learn/anatomy-of-incentive-mechanism.md b/docs/learn/anatomy-of-incentive-mechanism.md
index d2b92f63c..f9f90e761 100644
--- a/docs/learn/anatomy-of-incentive-mechanism.md
+++ b/docs/learn/anatomy-of-incentive-mechanism.md
@@ -6,7 +6,7 @@ title: "Understanding Incentive Mechanisms"
This page explores the concept and usage of incentive mechanisms in Bittensor.
-See [Components of the Bittensor platform](../learn/bittensor-building-blocks) for an explanation of the basics, such as subnets, miners, validators, and the role of the blockchain.
+See [Components of the Bittensor platform](../learn/neurons) for an explanation of the basics, such as subnets, miners, validators, and the role of the blockchain.
Each subnet has its own *incentive mechanism*, a scoring model that drives the behavior of its participants, and the production of the subnet's digital commodity, by defining **how validators are to evaluate miners’ work**. Miners are incentivized to optimize for this model so validators will score (or 'weight') their work highly, resulting in higher emissions. Validators are incentivized to accurately score miners' work according to the model because the algorithm penalizes departure from consensus in miner scores with lower emissions.
@@ -63,7 +63,7 @@ A subnet incentive mechanism must provide the following:
A subnet creator must define a protocol for how validators are to query miners, and how miners should respond. Protocols are built using the Axon-Dendrite client-server model and Synapse data objects.
-See [Neuron to neuron communication](./bittensor-building-blocks.md#neuron-to-neuron-communication).
+See [Neuron to neuron communication](./neurons.md#neuron-to-neuron-communication).
### Subnet task
diff --git a/docs/learn/bittensor-building-blocks.md b/docs/learn/neurons.md
similarity index 100%
rename from docs/learn/bittensor-building-blocks.md
rename to docs/learn/neurons.md
diff --git a/docs/local-build/create-subnet.md b/docs/local-build/create-subnet.md
index f6ed712a5..e2c416f1a 100644
--- a/docs/local-build/create-subnet.md
+++ b/docs/local-build/create-subnet.md
@@ -15,13 +15,12 @@ Prerequisites:
## Create subnet
-To access the handy pre-provisioned development "Alice" account on your local chain, use:
```shell
btcli subnet create \
--subnet-name awesome-first-subnet \
--wallet.name sn-creator \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
### Trouble shoot
#### Insufficient funds
@@ -42,7 +41,7 @@ btcli wallet transfer \
--amount 1001 \
--wallet.name alice \
--destination "5C9xw4..." \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
@@ -97,7 +96,7 @@ To remedy your liquidity shortfall, transfer $\tau$ from the Alice account and t
--amount 1001 \
--wallet.name alice \
--destination "5GVsCAY6RuSuoAA1E77xsHJ9PjdZJjJrRkNFDxVtRKPnw7TR" \
- --subtensor.chain_endpoint ws://127.0.0.1:9945
+ --network ws://127.0.0.1:9945
```
```shell
@@ -117,7 +116,7 @@ For example:
btcli subnet create \
--subnet-name awesome-first-subnet \
--wallet.name sn-creator \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
```console
Subnet burn cost: τ 1,000.0000
@@ -133,7 +132,7 @@ Decrypting...
btcli subnet create \
--subnet-name awesome-second-subnet \
--wallet.name sn-creator \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
```console
@@ -151,7 +150,7 @@ Decrypting...
```shell
btcli subnet list \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
```console
Subnets
diff --git a/docs/local-build/deploy.md b/docs/local-build/deploy.md
index 3594d8b88..eab779c8d 100644
--- a/docs/local-build/deploy.md
+++ b/docs/local-build/deploy.md
@@ -91,7 +91,7 @@ Ensure your local chain is working by checking the list of subnets.
Note the use of the `--chain_endpoint` flag to target the local chain, rather than, say, test network
```shell
- btcli subnet list --subtensor.chain_endpoint ws://127.0.0.1:9945
+ btcli subnet list --network ws://127.0.0.1:9945
btcli subnet list --network test
```
diff --git a/docs/local-build/mine-validate.md b/docs/local-build/mine-validate.md
index 9c0efd443..1f85dfab8 100644
--- a/docs/local-build/mine-validate.md
+++ b/docs/local-build/mine-validate.md
@@ -21,14 +21,14 @@ Register the subnet miner and validator with the following commands:
btcli subnet register \
--wallet.name validator \
--wallet.hotkey default \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
```bash
btcli subnet register \
--netuid 2 \
--wallet.name miner \
--wallet.hotkey default \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
@@ -47,7 +47,7 @@ btcli wallet transfer \
--amount 11 \
--wallet.name alice \
--destination "5EEy34..." \
---subtensor.chain_endpoint ws://127.0.0.1:9945
+--network ws://127.0.0.1:9945
```
@@ -82,9 +82,9 @@ Balance:
Confirm your registration on the subnet with the following command:
```shell
-btcli wallet overview --wallet.name validator --subtensor.chain_endpoint ws://127.0.0.1:9945
+btcli wallet overview --wallet.name validator --network ws://127.0.0.1:9945
-btcli wallet overview --wallet.name miner --subtensor.chain_endpoint ws://127.0.0.1:9945
+btcli wallet overview --wallet.name miner --network ws://127.0.0.1:9945
```
diff --git a/docs/local-build/provision-wallets.md b/docs/local-build/provision-wallets.md
index 89611deee..3ba4cb238 100644
--- a/docs/local-build/provision-wallets.md
+++ b/docs/local-build/provision-wallets.md
@@ -18,7 +18,7 @@ btcli wallet create --uri alice
Confirm Alice's massive $\tau$ bag.
```shell
- btcli w balance --wallet.name alice --subtensor.chain_endpoint ws://127.0.0.1:9945
+ btcli w balance --wallet.name alice --network ws://127.0.0.1:9945
```
```console
diff --git a/docs/miners/index.md b/docs/miners/index.md
index 4840719d3..a2d0d9c2e 100644
--- a/docs/miners/index.md
+++ b/docs/miners/index.md
@@ -64,7 +64,7 @@ Typically, subnets have 256 UID slots, with a maximum of 64 slots capable of ser
Deregistration only occurs on subnets where all 256 UID slots are occupied. If a new registration occurs in a subnet with available UID slots, the registered neuron occupies one of the available UID slots.
:::
-Each tempo, the '[neuron](../learn/bittensor-building-blocks)' (miner _or_ validator node) with the lowest 'pruning score' (based solely on emissions), and that is no longer within its [immunity period](../subnets/subnet-hyperparameters.md#immunityperiod), risks being replaced by a newly registered neuron, who takes over that UID.
+Each tempo, the '[neuron](../learn/neurons)' (miner _or_ validator node) with the lowest 'pruning score' (based solely on emissions), and that is no longer within its [immunity period](../subnets/subnet-hyperparameters.md#immunityperiod), risks being replaced by a newly registered neuron, who takes over that UID.
:::info Deregistration is based on emissions
The subnet does not distinguish between miners and validators for the purpose of deregistration. The chain only looks at emissions (represented as 'pruning score'). Whenever a new registration occurs in the subnet, the neuron with the lowest emissions will get deregistered.
diff --git a/docs/reference/_bittensor-api-ref.md b/docs/reference/_bittensor-api-ref.md
index b24194346..15900f68a 100644
--- a/docs/reference/_bittensor-api-ref.md
+++ b/docs/reference/_bittensor-api-ref.md
@@ -14,7 +14,7 @@ The `Subtensor` is utilized for managing interactions with the subtensor chain.
# Creating a default chain connection to remote finney instance.
sub = bt.subtensor()
-# Parsing --subtensor.network and --subtensor.chain_endpoint from the command line
+# Parsing --subtensor.network and --network from the command line
sub = bt.subtensor( config = bt.subtensor.config() )
# Connecting subtensor's default local entrypoint "ws://127.0.0.1:9944"
diff --git a/docs/staking-and-delegation/managing-stake-btcli.md b/docs/staking-and-delegation/managing-stake-btcli.md
index 8f093a7a1..e6419a419 100644
--- a/docs/staking-and-delegation/managing-stake-btcli.md
+++ b/docs/staking-and-delegation/managing-stake-btcli.md
@@ -33,7 +33,8 @@ Test network tokens have no real value. Before managing liquidity on Bittensor m
## View TAO balance
-To stake, you'll first need some TAO. Inquire in [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553) to obtain TAO on Bittensor test network. Alternatively, you can transfer some testnet TAO to your wallet address using the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
+
+To stake, you'll first need some TAO. Inquire in [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553) to obtain TAO on Bittensor test network. Alternatively, you can obtain some by completing the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
After creating a wallet, ensure that you are targeting the test network by running the `btcli config set` command. Next, select network, and set it to `test`.
diff --git a/docs/staking-and-delegation/managing-stake-sdk.md b/docs/staking-and-delegation/managing-stake-sdk.md
index 6bc7e6cbb..8237117ea 100644
--- a/docs/staking-and-delegation/managing-stake-sdk.md
+++ b/docs/staking-and-delegation/managing-stake-sdk.md
@@ -19,7 +19,7 @@ Minimum transaction amount for stake/unstake/move/transfer: 500,000 RAO or 0.000
## Check your TAO balance
-To stake, you'll first need some TAO. Inquire in [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553) to obtain TAO on Bittensor test network. Alternatively, you can transfer some testnet TAO to your wallet address using the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
+To stake, you'll first need some TAO. Inquire in [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553) to obtain TAO on Bittensor test network. Alternatively, you can obtain some by completing the [BTCLI Live Coding Playground](../btcli/btcli-playground.md#transfer).
:::danger
The funds in a crypto wallet are only as secure as your private key and/or seed phrase, and the devices that have access to these.
diff --git a/docs/subnets/create-a-subnet.md b/docs/subnets/create-a-subnet.md
index 983ea2809..09a481e83 100644
--- a/docs/subnets/create-a-subnet.md
+++ b/docs/subnets/create-a-subnet.md
@@ -64,7 +64,7 @@ Subnet creations are limited to **one subnet creation per 7200 blocks** (approxi
## Prerequisites
- [Install the most recent version of BTCLI](../getting-started/install-btcli).
-- [Created a wallet](../getting-started/wallets.md#creating-a-local-wallet).
+- [Create a wallet](../getting-started/wallets.md#creating-a-local-wallet).
- To create a subnet on test chain, your wallet must have sufficient test net TAO. Inquire in [Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553) to obtain TAO on Bittensor test network.
- To create a subnet on main network (finney) requires a substantial investment of TAO, depending on current registration cost for new subnets.
diff --git a/docs/subnets/walkthrough-prompting.md b/docs/subnets/walkthrough-prompting.md
index c0e7ff8b2..da6d2cd26 100644
--- a/docs/subnets/walkthrough-prompting.md
+++ b/docs/subnets/walkthrough-prompting.md
@@ -25,7 +25,7 @@ You can see the prompting subnet in action on the [TAO.app explorer (select Subn
If you are new to Bittensor subnets and building blocks, read the following sections before you proceed further:
-- [Bittensor Building Blocks](../learn/bittensor-building-blocks).
+- [Bittensor Building Blocks](../learn/neurons).
- [Anatomy of Incentive Mechanism](../learn/anatomy-of-incentive-mechanism).
The below diagram shows a typical subnet with many subnet miners and subnet validators together executing the subnet incentive mechanism code. On the [TAO.app explorer (select Subnet 01: Text Prompting from the top menu)](https://tao.app) the **Metagraph** view for this Subnet 1: Prompting shows the performance details for each subnet miner and validator.
diff --git a/docs/tutorials/ocr-subnet-tutorial.md b/docs/tutorials/ocr-subnet-tutorial.md
index 5402367b9..f946955eb 100644
--- a/docs/tutorials/ocr-subnet-tutorial.md
+++ b/docs/tutorials/ocr-subnet-tutorial.md
@@ -53,7 +53,7 @@ For the rest of this tutorial we will proceed by demonstrating which blocks of P
If you are new to Bittensor, read the following sections before you proceed:
1. [Introduction](../learn/introduction.md) that describes how subnets form the heartbeat of the Bittensor network.
-2. [Bittensor Building Blocks](../learn/bittensor-building-blocks.md) that presents the basic building blocks you use to develop your subnet incentive mechanism.
+2. [Bittensor Building Blocks](../learn/neurons.md) that presents the basic building blocks you use to develop your subnet incentive mechanism.
3. [Anatomy of Incentive Mechanism](../learn/anatomy-of-incentive-mechanism.md) that introduces the general concept of a subnet incentive mechanism.
## OCR subnet summary
@@ -218,7 +218,7 @@ See the `OCRSynapse` class definition in [**ocr_subnet/protocol.py**](https://gi
```
:::tip Study tip
-See [Neuron-to-neuron communication](../learn/bittensor-building-blocks.md#neuron-to-neuron-communication).
+See [Neuron-to-neuron communication](../learn/neurons.md#neuron-to-neuron-communication).
:::
#### Send OCRSynapse to miners
diff --git a/docs/validators/index.md b/docs/validators/index.md
index 2c3906347..6126bbaeb 100644
--- a/docs/validators/index.md
+++ b/docs/validators/index.md
@@ -81,7 +81,7 @@ When a validator falls below the top 64 nodes by emissions, or has less than the
Deregistration only occurs on subnets where all 256 UID slots are occupied. If a new registration occurs in a subnet with available UID slots, the registered neuron occupies one of the available UID slots.
:::
-Each tempo, the '[neuron](../learn/bittensor-building-blocks)' (miner _or_ validator node) with the lowest 'pruning score' (based solely on emissions), and that is no longer within its [immunity period](../subnets/subnet-hyperparameters.md#immunityperiod), risks being replaced by a newly registered neuron, who takes over that UID.
+Each tempo, the '[neuron](../learn/neurons)' (miner _or_ validator node) with the lowest 'pruning score' (based solely on emissions), and that is no longer within its [immunity period](../subnets/subnet-hyperparameters.md#immunityperiod), risks being replaced by a newly registered neuron, who takes over that UID.
:::info Deregistration is based on emissions
The subnet does not distinguish between miners and validators for the purpose of deregistration. The chain only looks at emissions (represented as 'pruning score'). Whenever a new registration occurs in the subnet, the neuron with the lowest emissions will get deregistered.
diff --git a/sidebars.js b/sidebars.js
index 50671bc63..8fb3084e8 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -37,7 +37,7 @@ const sidebars = {
"learn/introduction",
"questions-and-answers",
"subnets/understanding-subnets",
- "learn/bittensor-building-blocks",
+ "learn/neurons",
"learn/anatomy-of-incentive-mechanism",
"emissions",
"yuma-consensus",
@@ -187,26 +187,53 @@ const sidebars = {
"subtensor-nodes/subtensor-storage-query-examples",
],
},
-
{
- type: "category",
- label: "EVM smart contracts on Bittensor",
- link: {type: "doc", id: "evm-tutorials/index",},
- collapsible: true,
- collapsed: true,
- items: [
- "evm-tutorials/evm-on-subtensor",
- "evm-tutorials/install",
- "evm-tutorials/evm-testnet-with-metamask-wallet",
+ type: 'category',
+ label: 'Bittensor EVM',
+ link: {type: "doc", id: "evm-tutorials/index"},
+ items:[
+ "evm-tutorials/index",
+ "evm-tutorials/subtensor-networks",
+
+ "evm-tutorials/evm-testnet-with-metamask-wallet",
"evm-tutorials/evm-localnet-with-metamask-wallet",
"evm-tutorials/evm-mainnet-with-metamask-wallet",
- "evm-tutorials/transfer-from-metamask-to-ss58",
- "evm-tutorials/transfer-between-two-h160-accounts",
"evm-tutorials/hardhat-config-for-subtensor-evm",
- "evm-tutorials/staking-precompile",
- "evm-tutorials/ed25519-verify-precompile",
- ],
- },
+ "evm-tutorials/remix-config-for-subtensor-evm",
+ "evm-tutorials/troubleshooting"
+ ]
+ },
+ {
+ type: 'category',
+ link: {type: "doc", id: "evm-tutorials/examples"},
+ label: 'Bittensor EVM: Examples and Precompiles',
+ items:[
+ "evm-tutorials/examples",
+ "evm-tutorials/convert-h160-to-ss58",
+ "evm-tutorials/ed25519-verify-precompile",
+ "evm-tutorials/transfer-from-metamask-to-ss58",
+ "evm-tutorials/transfer-between-two-h160-accounts",
+ "evm-tutorials/staking-precompile",
+ "evm-tutorials/subnet-precompile",
+ "evm-tutorials/metagraph-precompile",
+ "evm-tutorials/neuron-precompile",
+ "evm-tutorials/withdraw-from-alice",
+
+ ]
+ },
+ // {
+ // type: "category",
+ // label: "Bittensor EVM Smart Contracts",
+ // link: {type: "doc", id: "evm-tutorials/index",},
+ // collapsible: true,
+ // collapsed: true,
+ // items: [
+ // "evm-tutorials/install",
+ // ,
+
+
+ // ],
+ // },
{
type: "category",
label: "Governance",