Managing Stake with Bittensor Python SDK
This pages demonstrates usage of the Bittensor SDK for Python for managing stake.
TAO holders can stake any amount of the liquidity they hold to a validator. Also known as delegation, staking supports validators, because their total stake in the subnet, including stake delegated to them by others, determines their consensus power and their share of emissions. After the validator/delegate extracts their take the remaining emissions are credited back to the stakers/delegators in proportion to their stake with that validator.
See also:
Minimum transaction amount for stake/unstake/move/transfer: 500,000 RAO or 0.0005 TAO.
Check your TAO balance
To stake, you'll first need some TAO. Inquire in Discord to obtain TAO on Bittensor test network. Alternatively, you can obtain some by completing the BTCLI Live Coding Playground.
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.
Test network tokens have no real value. Before managing liquidity on Bittensor mainnet, carefully consider all aspects of secrets management and endpoint security!
import bittensor as bt
sub = bt.Subtensor(network="test")
wallet = bt.wallet(
name="PracticeKey!",
hotkey="stakinkey1",
)
wallet.unlock_coldkey()
balance = sub.get_balance(wallet.coldkey.ss58_address)
print(balance)
View exchange rates
The following script displays exchange rates for a subnet alpha token, with and without slippage.
import bittensor as bt
sub = bt.Subtensor(network="test")
subnet = sub.subnet(netuid=1)
print("alpha_to_tao_with_slippage", subnet.alpha_to_tao_with_slippage(100))
print("alpha_to_tao_with_slippage percentage", subnet.alpha_to_tao_with_slippage(100, percentage=True))
print("tao_to_alpha_with_slippage", subnet.tao_to_alpha_with_slippage(100))
print("tao_to_alpha_with_slippage percentage", subnet.tao_to_alpha_with_slippage(100, percentage=True))
print("tao_to_alpha", subnet.tao_to_alpha(100))
print("alpha_to_tao", subnet.alpha_to_tao(100))
Register on a subnet
You can register your hotkey on a subnet using the burned_register
method. This is necessary for staking, mining or validating.
import bittensor as bt
logging = bt.logging
logging.set_info()
sub = bt.Subtensor(network="test")
wallet = bt.wallet(
name="ExampleWalletName",
hotkey="ExampleHotkey",
)
wallet.unlock_coldkey()
reg = sub.burned_register(wallet=wallet, netuid=3)
View your registered subnets
import bittensor as bt
sub = bt.Subtensor(network="test")
wallet = bt.wallet(
name="ExampleWalletName",
hotkey="ExampleHotkey",
)
wallet.unlock_coldkey()
netuids = sub.get_netuids_for_hotkey(wallet.hotkey.ss58_address)
print(netuids)
Asynchronously stake to top subnets/validators
The following script incrementally stakes a user-defined amount of TAO in each of the user-defined number of the top subnets.
Note that it uses asynchronous calls to the Bittensor blockchain via the async_subtensor
module, employing the await asyncio.gather(*tasks)
pattern.
AsyncSubtensor
methods like add_stake()
, unstake()
, metagraph()
, and move_stake()
are designed as asynchronous methods, meaning that, unlike their Subtensor
module equivalents, they return coroutine objects that must be awaizted within an event loop.
import os, sys, asyncio
import bittensor as bt
import time
from bittensor import tao
# Load environmental variables
wallet_name=os.environ.get('WALLET')
total_to_stake=os.environ.get('TOTAL_TAO_TO_STAKE')
num_subnets= os.environ.get('NUM_SUBNETS_TO_STAKE_IN')
validators_per_subnet = os.environ.get('NUM_VALIDATORS_PER_SUBNET')
# Validate inputs
if wallet_name is None:
sys.exit("❌ WALLET not specified. Usage: `WALLET=my-wallet TOTAL_TAO_TO_STAKE=1 NUM_SUBNETS_TO_STAKE_IN=3 NUM_VALIDATORS_PER_SUBNET=3 python script.py`")
if total_to_stake is None:
print("⚠️ TOTAL_TAO_TO_STAKE not specified. Defaulting to 1 TAO.")
total_to_stake = 1.0
else:
try:
total_to_stake = float(total_to_stake)
except:
sys.exit("❌ Invalid TOTAL_TAO_TO_STAKE amount.")
if num_subnets is None:
num_subnets = 3
else:
try:
num_subnets = int(num_subnets)
except:
sys.exit("❌ Invalid NUM_SUBNETS_TO_STAKE_IN.")
if validators_per_subnet is None:
validators_per_subnet = 3
else:
try:
validators_per_subnet = int(validators_per_subnet)
except:
sys.exit("❌ Invalid NUM_VALIDATORS_PER_SUBNET.")
print(f"\n🔓 Using wallet: {wallet_name}")
print(f"📊 Dividing {total_to_stake} TAO across top {validators_per_subnet} validators in each of top {num_subnets} subnets.")
wallet = bt.wallet(wallet_name)
# Initialize the subtensor connection within a block scope to ensure it is garbage collected
async def stake_batch(subtensor, netuid, top_validators, amount_to_stake):
for hk in top_validators:
print(f"💰 Staking {amount_to_stake} to {hk} on subnet {netuid}...")
try:
results = await asyncio.gather(*[ subtensor.add_stake(wallet=wallet, netuid=netuid, hotkey_ss58=hk, amount=amount_to_stake) for hk in top_validators ] )
print(results)
except Exception as e:
print(f"❌ Failed to stake to {hk} on subnet {netuid}: {e}")
async def find_top_three_valis(subtensor,subnet):
netuid = subnet.netuid
print(f"\n🔍 Subnet {netuid} had {subnet.tao_in_emission} emissions!")
print(f"\n🔍 Fetching metagraph for subnet {netuid}...")
start_time = time.time()
metagraph = await subtensor.metagraph(netuid)
print(f"✅ Retrieved metagraph for subnet {netuid} in {time.time() - start_time:.2f} seconds.")
# Extract validators and their stake amounts
hk_stake_pairs = [(metagraph.hotkeys[index], metagraph.stake[index]) for index in range(len(metagraph.stake))]
# Sort validators by stake in descending order
top_validators = sorted(hk_stake_pairs, key=lambda x: x[1], reverse=True)[0:3]
# Print the top 3 validators for this subnet
print(f"\n🏆 Top 3 Validators for Subnet {netuid}:")
for rank, (index, stake) in enumerate(top_validators, start=1):
print(f" {rank}. Validator index {index} - Stake: {stake}")
return {
"netuid": netuid,
"metagraph": metagraph,
"validators": top_validators
}
async def main():
async with bt.async_subtensor(network='test') as subtensor:
print("Fetching information on top subnets by TAO emissions")
# get subnets and sort by tao emissions
sorted_subnets = sorted(list(await subtensor.all_subnets()), key=lambda subnet: subnet.tao_in_emission, reverse=True)
top_subnets = sorted_subnets[0:3]
amount_to_stake = bt.Balance.from_tao(total_to_stake/9)
# find the top 3 validators in each subnet
top_vali_dicts = await asyncio.gather(*[find_top_three_valis(subtensor, subnet) for subnet in top_subnets])
top_validators_per_subnet = {}
for d in top_vali_dicts:
netuid = d['netuid']
for v in d['validators']:
hk = v[0]
if netuid in top_validators_per_subnet:
top_validators_per_subnet[netuid].append(hk)
else:
top_validators_per_subnet[netuid] = [hk]
# Stake to each top 3 validators in each top 3 subnets
start_time = time.time()
await asyncio.gather(*[stake_batch(subtensor, netuid,top_validators, amount_to_stake) for netuid, top_validators in top_validators_per_subnet.items()])
print(f"Staking completed in {time.time() - start_time:.2f}s")
asyncio.run(main())
🔍 Using wallet: PracticeKey!
Staking total not specified, dividing 1 TAO across top 3 validators in each of top 3 subnets by default.
Usage: `TOTAL_TAO_TO STAKE=1 WALLET=my-wallet-name ./stakerscript.py`
Fetching information on top subnets by TAO emissions