Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
623384c
add `set_auto_stake_extrinsic`
Sep 22, 2025
41cc20b
update subtensors
Sep 22, 2025
dc61d95
update SubtensorApi
Sep 22, 2025
f74cf31
tests
Sep 22, 2025
9fd0b6c
Merge branch 'staging' into feat/roman/autostaking
Sep 22, 2025
936dad4
Add initial info for the script
thewhaleking Sep 23, 2025
2e3ccb9
More thoughts.
thewhaleking Sep 23, 2025
fa1590f
Bump asi req
thewhaleking Sep 24, 2025
8b5ee1a
WIP
thewhaleking Sep 24, 2025
ea456e8
Working well
thewhaleking Sep 24, 2025
b9dff9f
Updated test with new data
thewhaleking Sep 24, 2025
8dcbb1d
Handle "jsonrpc" replacement
thewhaleking Sep 24, 2025
664d687
Updated metadata and metadatav15 to latest.
thewhaleking Sep 24, 2025
c34cc2c
test_get_all_subnets_info data + test
thewhaleking Sep 25, 2025
f6b5ac2
get_netuids_for_hotkey
thewhaleking Sep 25, 2025
690d34f
get_current_block
thewhaleking Sep 25, 2025
e289c0b
Remove logs before writing to them and parsing what is written
thewhaleking Sep 25, 2025
941e9fd
More tests + script improvement
thewhaleking Sep 25, 2025
fdc7af5
More tests
thewhaleking Sep 25, 2025
1e1a4b9
More tests
thewhaleking Sep 25, 2025
ccea208
More tests
thewhaleking Sep 25, 2025
ab9170a
Final test updated
thewhaleking Sep 25, 2025
5540aa6
Trying something
thewhaleking Sep 26, 2025
9cd8e41
In the arena
thewhaleking Sep 26, 2025
c9117fd
In the arena
thewhaleking Sep 26, 2025
55deddc
In the arena
thewhaleking Sep 26, 2025
24715b6
In the arena
thewhaleking Sep 26, 2025
8562239
Merge branch 'staging' into feat/thewhaleking/websocket-script
thewhaleking Sep 26, 2025
264262d
Brilliant me
thewhaleking Sep 26, 2025
7f1da8e
TODONE
thewhaleking Sep 26, 2025
2b77bb3
Cleanup
thewhaleking Sep 26, 2025
716f202
Added comments
thewhaleking Sep 26, 2025
520108b
Merge pull request #3067 from opentensor/feat/thewhaleking/websocket-…
basfroman Sep 29, 2025
20e2dc5
Merge branch 'staging' into feat/roman/autostaking
basfroman Oct 1, 2025
d21e07e
improve tests regardless last logic changes
basfroman Oct 1, 2025
130b619
docstrings
basfroman Oct 1, 2025
425b64e
Merge pull request #3062 from opentensor/feat/roman/autostaking
basfroman Oct 1, 2025
b5e8958
Merge pull request #3081 from opentensor/master
basfroman Oct 5, 2025
c921cc7
Updat get_neuron_for_pubkey_and_subnet to remove block
yuma-doc Oct 6, 2025
b65bc43
Merge pull request #3084 from yuma-doc/patch-1
basfroman Oct 7, 2025
1248a72
Adds comment for the future
thewhaleking Oct 7, 2025
60d7451
Improves the `determine_block_hash` method
thewhaleking Oct 7, 2025
9cce675
Adds unit test
thewhaleking Oct 7, 2025
a6d8d5a
PR suggestions
thewhaleking Oct 7, 2025
365f291
typo
thewhaleking Oct 7, 2025
4182d96
Merge pull request #3087 from opentensor/chore/thewhaleking/dev-comment
basfroman Oct 7, 2025
d950213
bump version and update CHANGELOG.md
basfroman Oct 8, 2025
3c1efb8
Merge pull request #3088 from opentensor/changelog/9.12.0
basfroman Oct 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 9.12.0 /2025-10-08

## What's Changed
* Websocket Stub Generator Script by @thewhaleking in https://github.com/opentensor/bittensor/pull/3067
* set auto hotkey per subnet by @basfroman in https://github.com/opentensor/bittensor/pull/3062
* Update `get_neuron_for_pubkey_and_subnet` to use block_hash instead of block and block_hash by @yuma-doc in https://github.com/opentensor/bittensor/pull/3084
* Improves `determine_block_hash` method of `AsyncSubtensor` by @thewhaleking in https://github.com/opentensor/bittensor/pull/3087

## New Contributors
* @yuma-doc made their first contribution in https://github.com/opentensor/bittensor/pull/3084

**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.11.1...v9.12.0

## 9.11.1 /2025-09-25

## What's Changed
Expand Down
114 changes: 100 additions & 14 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
from bittensor.core.extrinsics.asyncex.staking import (
add_stake_extrinsic,
add_stake_multiple_extrinsic,
set_auto_stake_extrinsic,
)
from bittensor.core.extrinsics.asyncex.start_call import start_call_extrinsic
from bittensor.core.extrinsics.asyncex.mechanism import (
Expand Down Expand Up @@ -307,7 +308,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):

async def determine_block_hash(
self,
block: Optional[int],
block: Optional[int] = None,
block_hash: Optional[str] = None,
reuse_block: bool = False,
) -> Optional[str]:
Expand All @@ -317,31 +318,41 @@ async def determine_block_hash(
for blockchain queries.

Arguments:
block: The block number to get the hash for. Do not specify if using block_hash or reuse_block.
block_hash: The hash of the blockchain block. Do not specify if using block or reuse_block.
reuse_block: Whether to reuse the last-used block hash. Do not set if using block or reuse_block.
block: The block number to get the hash for. If specifying along with `block_hash`, the hash of `block` will
be checked and compared with the supplied block hash, raising a ValueError if the two do not match.
block_hash: The hash of the blockchain block. If specifying along with `block`, the hash of `block` will be
checked and compared with the supplied block hash, raising a ValueError if the two do not match.
reuse_block: Whether to reuse the last-used block hash. Do not set if using block or block_hash.

Returns:
Optional[str]: The block hash if one can be determined, None otherwise.

Raises:
ValueError: If more than one of block, block_hash, or reuse_block is specified.
ValueError: If reuse_block is set, while also supplying a block/block_hash, or if supplying a block and
block_hash, but the hash of the block does not match the supplied block hash.

Example:
# Get hash for specific block
block_hash = await subtensor.determine_block_hash(block=1000000)

# Use provided block hash
hash = await subtensor.determine_block_hash(block_hash="0x1234...")
block_hash = await subtensor.determine_block_hash(block_hash="0x1234...")

# Reuse last block hash
hash = await subtensor.determine_block_hash(reuse_block=True)
"""
# Ensure that only one of the parameters is specified.
if sum(bool(x) for x in [block, block_hash, reuse_block]) > 1:
raise ValueError(
"Only one of ``block``, ``block_hash``, or ``reuse_block`` can be specified."
)
block_hash = await subtensor.determine_block_hash(reuse_block=True)
"""
if reuse_block and any([block, block_hash]):
raise ValueError("Cannot specify both reuse_block and block_hash/block")
if block and block_hash:
retrieved_block_hash = await self.get_block_hash(block)
if retrieved_block_hash != block_hash:
raise ValueError(
"You have supplied a `block_hash` and a `block`, but the block does not map to the same hash as "
f"the one you supplied. You supplied `block_hash={block_hash}` for `block={block}`, but this block"
f"maps to the block hash {retrieved_block_hash}."
)
else:
return retrieved_block_hash

# Return the appropriate value.
if block_hash:
Expand Down Expand Up @@ -1201,6 +1212,42 @@ async def get_all_subnets_info(

return SubnetInfo.list_from_dicts(result)

async def get_auto_stakes(
self,
coldkey_ss58: str,
block: Optional[int] = None,
block_hash: Optional[str] = None,
reuse_block: bool = False,
) -> dict[int, str]:
"""Fetches auto stake destinations for a given wallet across all subnets.

Parameters:
coldkey_ss58: Coldkey ss58 address.
block: The block number for the query.
block_hash: The block hash for the query.
reuse_block: Whether to reuse the last-used block hash.

Returns:
dict[int, str]:
- netuid: The unique identifier of the subnet.
- hotkey: The hotkey of the wallet.
"""
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
query = await self.substrate.query_map(
module="SubtensorModule",
storage_function="AutoStakeDestination",
params=[coldkey_ss58],
block_hash=block_hash,
)

pairs = {}
async for netuid, destination in query:
hotkey_ss58 = decode_account_id(destination.value[0])
if hotkey_ss58:
pairs[int(netuid)] = hotkey_ss58

return pairs

async def get_balance(
self,
address: str,
Expand Down Expand Up @@ -2530,7 +2577,6 @@ async def get_neuron_for_pubkey_and_subnet(
return await self.neuron_for_uid(
uid=uid,
netuid=netuid,
block=block,
block_hash=block_hash,
reuse_block=reuse_block,
)
Expand Down Expand Up @@ -5170,6 +5216,46 @@ async def root_set_weights(
period=period,
)

async def set_auto_stake(
self,
wallet: "Wallet",
netuid: int,
hotkey_ss58: str,
period: Optional[int] = None,
raise_error: bool = False,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
) -> tuple[bool, str]:
"""Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism.

Parameters:
wallet: Bittensor Wallet instance.
netuid: The subnet unique identifier.
hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards
received from the specified subnet immediately upon receipt.
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
transaction is not included in a block within that number of blocks, it will expire and be rejected. You
can think of it as an expiration date for the transaction.
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
wait_for_finalization: Whether to wait for the finalization of the transaction.

Returns:
tuple[bool, str]:
`True` if the extrinsic executed successfully, `False` otherwise.
`message` is a string value describing the success or potential error.
"""
return await set_auto_stake_extrinsic(
subtensor=self,
wallet=wallet,
netuid=netuid,
hotkey_ss58=hotkey_ss58,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

async def set_children(
self,
wallet: "Wallet",
Expand Down
68 changes: 68 additions & 0 deletions bittensor/core/extrinsics/asyncex/staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,71 @@ async def add_stake_multiple_extrinsic(
return True

return False


async def set_auto_stake_extrinsic(
subtensor: "AsyncSubtensor",
wallet: "Wallet",
netuid: int,
hotkey_ss58: str,
period: Optional[int] = None,
raise_error: bool = False,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
) -> tuple[bool, str]:
"""Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism.

Parameters:
subtensor: AsyncSubtensor instance.
wallet: Bittensor Wallet instance.
netuid: The subnet unique identifier.
hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards
received from the specified subnet immediately upon receipt.
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
transaction is not included in a block within that number of blocks, it will expire and be rejected. You can
think of it as an expiration date for the transaction.
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
wait_for_finalization: Whether to wait for the finalization of the transaction.

Returns:
tuple[bool, str]:
`True` if the extrinsic executed successfully, `False` otherwise.
`message` is a string value describing the success or potential error.
"""
try:
unlock = unlock_key(wallet, raise_error=raise_error)
if not unlock.success:
logging.error(unlock.message)
return False, unlock.message

call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="set_coldkey_auto_stake_hotkey",
call_params={
"netuid": netuid,
"hotkey": hotkey_ss58,
},
)
success, message = await subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

if success:
logging.debug(message)
return True, message

logging.error(message)
return False, message

except Exception as error:
if raise_error:
raise error
logging.error(str(error))

return False, str(error)
68 changes: 68 additions & 0 deletions bittensor/core/extrinsics/staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,71 @@ def add_stake_multiple_extrinsic(
return True

return False


def set_auto_stake_extrinsic(
subtensor: "Subtensor",
wallet: "Wallet",
netuid: int,
hotkey_ss58: str,
period: Optional[int] = None,
raise_error: bool = False,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
) -> tuple[bool, str]:
"""Sets the coldkey to automatically stake to the hotkey within specific subnet mechanism.

Parameters:
subtensor: AsyncSubtensor instance.
wallet: Bittensor Wallet instance.
netuid: The subnet unique identifier.
hotkey_ss58: The SS58 address of the validator's hotkey to which the miner automatically stakes all rewards
received from the specified subnet immediately upon receipt.
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
transaction is not included in a block within that number of blocks, it will expire and be rejected. You can
think of it as an expiration date for the transaction.
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
wait_for_finalization: Whether to wait for the finalization of the transaction.

Returns:
tuple[bool, str]:
`True` if the extrinsic executed successfully, `False` otherwise.
`message` is a string value describing the success or potential error.
"""
try:
unlock = unlock_key(wallet, raise_error=raise_error)
if not unlock.success:
logging.error(unlock.message)
return False, unlock.message

call = subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="set_coldkey_auto_stake_hotkey",
call_params={
"netuid": netuid,
"hotkey": hotkey_ss58,
},
)
success, message = subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
period=period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

if success:
logging.debug(message)
return True, message

logging.error(message)
return False, message

except Exception as error:
if raise_error:
raise error
logging.error(str(error))

return False, str(error)
Loading
Loading