bittensor 10.3.2__tar.gz → 10.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. {bittensor-10.3.2 → bittensor-10.4.0}/PKG-INFO +3 -3
  2. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/async_subtensor.py +353 -0
  3. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/errors.py +2 -0
  4. bittensor-10.4.0/bittensor/core/extrinsics/asyncex/lock.py +238 -0
  5. bittensor-10.4.0/bittensor/core/extrinsics/lock.py +239 -0
  6. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/subtensor_module.py +60 -0
  7. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/subtensor.py +312 -1
  8. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/types.py +15 -0
  9. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/extrinsics.py +11 -5
  10. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/staking.py +11 -0
  11. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor.egg-info/PKG-INFO +3 -3
  12. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor.egg-info/SOURCES.txt +2 -0
  13. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor.egg-info/requires.txt +2 -2
  14. {bittensor-10.3.2 → bittensor-10.4.0}/pyproject.toml +3 -3
  15. {bittensor-10.3.2 → bittensor-10.4.0}/LICENSE +0 -0
  16. {bittensor-10.3.2 → bittensor-10.4.0}/README.md +0 -0
  17. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/__init__.py +0 -0
  18. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/__main__.py +0 -0
  19. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/__init__.py +0 -0
  20. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/axon.py +0 -0
  21. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/__init__.py +0 -0
  22. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/axon_info.py +0 -0
  23. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/chain_identity.py +0 -0
  24. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/coldkey_swap.py +0 -0
  25. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/crowdloan_info.py +0 -0
  26. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/delegate_info.py +0 -0
  27. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/delegate_info_lite.py +0 -0
  28. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/dynamic_info.py +0 -0
  29. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/info_base.py +0 -0
  30. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/ip_info.py +0 -0
  31. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/metagraph_info.py +0 -0
  32. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/neuron_info.py +0 -0
  33. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/neuron_info_lite.py +0 -0
  34. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/prometheus_info.py +0 -0
  35. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/proposal_vote_data.py +0 -0
  36. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/proxy.py +0 -0
  37. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/root_claim.py +0 -0
  38. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/scheduled_coldkey_swap_info.py +0 -0
  39. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/sim_swap.py +0 -0
  40. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/stake_info.py +0 -0
  41. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/subnet_hyperparameters.py +0 -0
  42. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/subnet_identity.py +0 -0
  43. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/subnet_info.py +0 -0
  44. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/subnet_state.py +0 -0
  45. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/utils.py +0 -0
  46. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/chain_data/weight_commit_info.py +0 -0
  47. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/config.py +0 -0
  48. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/dendrite.py +0 -0
  49. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/__init__.py +0 -0
  50. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/__init__.py +0 -0
  51. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/children.py +0 -0
  52. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/coldkey_swap.py +0 -0
  53. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/crowdloan.py +0 -0
  54. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/liquidity.py +0 -0
  55. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/mev_shield.py +0 -0
  56. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/move_stake.py +0 -0
  57. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/proxy.py +0 -0
  58. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/registration.py +0 -0
  59. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/root.py +0 -0
  60. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/serving.py +0 -0
  61. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/staking.py +0 -0
  62. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/start_call.py +0 -0
  63. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/sudo.py +0 -0
  64. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/take.py +0 -0
  65. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/transfer.py +0 -0
  66. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/unstaking.py +0 -0
  67. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/utils.py +0 -0
  68. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/asyncex/weights.py +0 -0
  69. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/children.py +0 -0
  70. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/coldkey_swap.py +0 -0
  71. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/crowdloan.py +0 -0
  72. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/liquidity.py +0 -0
  73. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/mev_shield.py +0 -0
  74. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/move_stake.py +0 -0
  75. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/__init__.py +0 -0
  76. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/admin_utils.py +0 -0
  77. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/balances.py +0 -0
  78. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/base.py +0 -0
  79. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/commitments.py +0 -0
  80. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/crowdloan.py +0 -0
  81. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/mev_shield.py +0 -0
  82. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/proxy.py +0 -0
  83. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/sudo.py +0 -0
  84. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/pallets/swap.py +0 -0
  85. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/proxy.py +0 -0
  86. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/registration.py +0 -0
  87. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/root.py +0 -0
  88. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/serving.py +0 -0
  89. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/staking.py +0 -0
  90. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/start_call.py +0 -0
  91. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/sudo.py +0 -0
  92. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/take.py +0 -0
  93. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/transfer.py +0 -0
  94. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/unstaking.py +0 -0
  95. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/utils.py +0 -0
  96. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/extrinsics/weights.py +0 -0
  97. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/metagraph.py +0 -0
  98. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/settings.py +0 -0
  99. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/stream.py +0 -0
  100. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/synapse.py +0 -0
  101. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/tensor.py +0 -0
  102. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/core/threadpool.py +0 -0
  103. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/__init__.py +0 -0
  104. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/__init__.py +0 -0
  105. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/calls/__init__.py +0 -0
  106. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/calls/non_sudo_calls.py +0 -0
  107. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/calls/pallets.py +0 -0
  108. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/calls/sudo_calls.py +0 -0
  109. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/subnet.py +0 -0
  110. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/dev_framework/utils.py +0 -0
  111. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/__init__.py +0 -0
  112. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/chain.py +0 -0
  113. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/commitments.py +0 -0
  114. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/crowdloans.py +0 -0
  115. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/delegates.py +0 -0
  116. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/metagraphs.py +0 -0
  117. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/mev_shield.py +0 -0
  118. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/neurons.py +0 -0
  119. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/proxy.py +0 -0
  120. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/queries.py +0 -0
  121. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/subnets.py +0 -0
  122. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/utils.py +0 -0
  123. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/subtensor_api/wallets.py +0 -0
  124. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/extras/timelock.py +0 -0
  125. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/py.typed +0 -0
  126. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/__init__.py +0 -0
  127. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/axon_utils.py +0 -0
  128. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/balance.py +0 -0
  129. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/__init__.py +0 -0
  130. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/console.py +0 -0
  131. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/defines.py +0 -0
  132. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/format.py +0 -0
  133. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/helpers.py +0 -0
  134. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/levels.py +0 -0
  135. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/btlogging/loggingmachine.py +0 -0
  136. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/certifi.sh +0 -0
  137. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/easy_imports.py +0 -0
  138. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/formatting.py +0 -0
  139. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/liquidity.py +0 -0
  140. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/mock/__init__.py +0 -0
  141. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/mock/subtensor_mock.py +0 -0
  142. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/networking.py +0 -0
  143. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/registration/__init__.py +0 -0
  144. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/registration/torch_utils.py +0 -0
  145. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/subnets.py +0 -0
  146. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/version.py +0 -0
  147. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor/utils/weight_utils.py +0 -0
  148. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor.egg-info/dependency_links.txt +0 -0
  149. {bittensor-10.3.2 → bittensor-10.4.0}/bittensor.egg-info/top_level.txt +0 -0
  150. {bittensor-10.3.2 → bittensor-10.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bittensor
3
- Version: 10.3.2
3
+ Version: 10.4.0
4
4
  Summary: Bittensor SDK
5
5
  Author: bittensor.com
6
6
  License: The MIT License (MIT)
@@ -44,7 +44,7 @@ Requires-Dist: pydantic<3,>=2.3
44
44
  Requires-Dist: cyscale<1.0.0,>=0.3.3
45
45
  Requires-Dist: uvicorn
46
46
  Requires-Dist: bittensor-drand<2.0.0,>=1.3.0
47
- Requires-Dist: bittensor-wallet==4.0.1
47
+ Requires-Dist: bittensor-wallet>=4.1.0
48
48
  Requires-Dist: async-substrate-interface<3.0.0,>=2.0.4
49
49
  Provides-Extra: dev
50
50
  Requires-Dist: pytest==9.0.3; extra == "dev"
@@ -69,7 +69,7 @@ Requires-Dist: types-requests; extra == "dev"
69
69
  Provides-Extra: torch
70
70
  Requires-Dist: torch<3.0,>=1.13.1; extra == "torch"
71
71
  Provides-Extra: cli
72
- Requires-Dist: bittensor-cli>=9.21.1; extra == "cli"
72
+ Requires-Dist: bittensor-cli>=9.22.0; extra == "cli"
73
73
  Dynamic: license-file
74
74
 
75
75
  <div align="center">
@@ -81,6 +81,11 @@ from bittensor.core.extrinsics.asyncex.liquidity import (
81
81
  toggle_user_liquidity_extrinsic,
82
82
  )
83
83
  from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic
84
+ from bittensor.core.extrinsics.asyncex.lock import (
85
+ lock_stake_extrinsic,
86
+ move_lock_extrinsic,
87
+ set_perpetual_lock_extrinsic,
88
+ )
84
89
  from bittensor.core.extrinsics.asyncex.move_stake import (
85
90
  move_stake_extrinsic,
86
91
  swap_stake_extrinsic,
@@ -147,6 +152,7 @@ from bittensor.core.settings import (
147
152
  from bittensor.core.types import (
148
153
  BlockInfo,
149
154
  ExtrinsicResponse,
155
+ LockState,
150
156
  Salt,
151
157
  SubtensorMixin,
152
158
  UIDs,
@@ -1940,6 +1946,47 @@ class AsyncSubtensor(SubtensorMixin):
1940
1946
  cooldown,
1941
1947
  )
1942
1948
 
1949
+ async def get_coldkey_lock(
1950
+ self,
1951
+ coldkey_ss58: str,
1952
+ netuid: int,
1953
+ block: Optional[int] = None,
1954
+ block_hash: Optional[str] = None,
1955
+ reuse_block: bool = False,
1956
+ ) -> Optional["LockState"]:
1957
+ """
1958
+ Returns the current lock for a coldkey on a subnet, rolled forward to the current block.
1959
+
1960
+ Unlike get_stake_lock which returns the raw stored state, this method applies decay to return the actual current
1961
+ lock values accounting for time elapsed since last update.
1962
+
1963
+ Parameters:
1964
+ coldkey_ss58: The SS58 address of the coldkey.
1965
+ netuid: The subnet UID to query.
1966
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
1967
+ block_hash: The block hash at which to query.
1968
+ reuse_block: Whether to reuse the last-used block hash.
1969
+
1970
+ Returns:
1971
+ LockState with current locked_mass, conviction, and last_update, or None if no lock exists.
1972
+ """
1973
+ result = await self.query_runtime_api(
1974
+ runtime_api="StakeInfoRuntimeApi",
1975
+ method="get_coldkey_lock",
1976
+ params=[coldkey_ss58, netuid],
1977
+ block=block,
1978
+ block_hash=block_hash,
1979
+ reuse_block=reuse_block,
1980
+ )
1981
+ if result is None:
1982
+ return None
1983
+
1984
+ return LockState(
1985
+ locked_mass=Balance.from_rao(result["locked_mass"], netuid),
1986
+ conviction=fixed_to_float(result["conviction"]),
1987
+ last_update=int(result["last_update"]),
1988
+ )
1989
+
1943
1990
  async def get_coldkey_swap_announcement(
1944
1991
  self,
1945
1992
  coldkey_ss58: str,
@@ -2781,6 +2828,39 @@ class AsyncSubtensor(SubtensorMixin):
2781
2828
  # TODO verify this from rao, seems like we're just rounding down
2782
2829
  return block_updated, Balance.from_rao(ema_value)
2783
2830
 
2831
+ async def get_hotkey_conviction(
2832
+ self,
2833
+ hotkey_ss58: str,
2834
+ netuid: int,
2835
+ block: Optional[int] = None,
2836
+ block_hash: Optional[str] = None,
2837
+ reuse_block: bool = False,
2838
+ ) -> float:
2839
+ """
2840
+ Gets the total conviction score for a hotkey on a subnet.
2841
+
2842
+ Parameters:
2843
+ hotkey_ss58: The SS58 address of the hotkey to query.
2844
+ netuid: The subnet UID to query.
2845
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
2846
+ block_hash: The block hash at which to query.
2847
+ reuse_block: Whether to reuse the last-used block hash.
2848
+
2849
+ Returns:
2850
+ The conviction score as a float.
2851
+ """
2852
+ result = await self.query_runtime_api(
2853
+ runtime_api="StakeInfoRuntimeApi",
2854
+ method="get_hotkey_conviction",
2855
+ params=[hotkey_ss58, netuid],
2856
+ block=block,
2857
+ block_hash=block_hash,
2858
+ reuse_block=reuse_block,
2859
+ )
2860
+ if result is None:
2861
+ return 0.0
2862
+ return fixed_to_float(result)
2863
+
2784
2864
  async def get_hotkey_owner(
2785
2865
  self,
2786
2866
  hotkey_ss58: str,
@@ -3359,6 +3439,34 @@ class AsyncSubtensor(SubtensorMixin):
3359
3439
 
3360
3440
  return Balance.from_rao(result.value or 0)
3361
3441
 
3442
+ async def get_most_convicted_hotkey_on_subnet(
3443
+ self,
3444
+ netuid: int,
3445
+ block: Optional[int] = None,
3446
+ block_hash: Optional[str] = None,
3447
+ reuse_block: bool = False,
3448
+ ) -> Optional[str]:
3449
+ """
3450
+ Gets the hotkey with the highest conviction score on a subnet (the "subnet king").
3451
+
3452
+ Parameters:
3453
+ netuid: The subnet UID to query.
3454
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
3455
+ block_hash: The block hash at which to query.
3456
+ reuse_block: Whether to reuse the last-used block hash.
3457
+
3458
+ Returns:
3459
+ The SS58 address of the most convicted hotkey, or None if no locks exist.
3460
+ """
3461
+ return await self.query_runtime_api(
3462
+ runtime_api="StakeInfoRuntimeApi",
3463
+ method="get_most_convicted_hotkey_on_subnet",
3464
+ params=[netuid],
3465
+ block=block,
3466
+ block_hash=block_hash,
3467
+ reuse_block=reuse_block,
3468
+ )
3469
+
3362
3470
  async def get_netuids_for_hotkey(
3363
3471
  self,
3364
3472
  hotkey_ss58: str,
@@ -4194,6 +4302,88 @@ class AsyncSubtensor(SubtensorMixin):
4194
4302
  )
4195
4303
  return sim_swap_result.tao_fee
4196
4304
 
4305
+ async def get_stake_lock(
4306
+ self,
4307
+ coldkey_ss58: str,
4308
+ netuid: int,
4309
+ hotkey_ss58: str,
4310
+ block: Optional[int] = None,
4311
+ block_hash: Optional[str] = None,
4312
+ reuse_block: bool = False,
4313
+ ) -> Optional["LockState"]:
4314
+ """
4315
+ Retrieves the lock state for a specific coldkey-netuid-hotkey combination.
4316
+
4317
+ Parameters:
4318
+ coldkey_ss58: The SS58 address of the coldkey that owns the lock.
4319
+ netuid: The subnet UID on which to query.
4320
+ hotkey_ss58: The SS58 address of the hotkey the lock is on.
4321
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
4322
+ block_hash: The block hash at which to query.
4323
+ reuse_block: Whether to reuse the last-used block hash.
4324
+
4325
+ Returns:
4326
+ LockState dict with locked_mass, conviction, last_update, or None if no lock exists.
4327
+ """
4328
+ query = await self.query_subtensor(
4329
+ "Lock",
4330
+ params=[coldkey_ss58, netuid, hotkey_ss58],
4331
+ block=block,
4332
+ block_hash=block_hash,
4333
+ reuse_block=reuse_block,
4334
+ )
4335
+ if query.value is None:
4336
+ return None
4337
+
4338
+ value = cast(dict, query.value)
4339
+ return LockState(
4340
+ locked_mass=Balance.from_rao(value["locked_mass"], netuid),
4341
+ conviction=fixed_to_float(value["conviction"]),
4342
+ last_update=int(value["last_update"]),
4343
+ )
4344
+
4345
+ async def get_stake_locks(
4346
+ self,
4347
+ coldkey_ss58: str,
4348
+ netuid: int,
4349
+ block: Optional[int] = None,
4350
+ block_hash: Optional[str] = None,
4351
+ reuse_block: bool = False,
4352
+ ) -> "list[tuple[str, LockState]]":
4353
+ """
4354
+ Retrieves all lock states for a coldkey on a subnet.
4355
+
4356
+ Parameters:
4357
+ coldkey_ss58: The SS58 address of the coldkey that owns the locks.
4358
+ netuid: The subnet UID on which to query.
4359
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
4360
+ block_hash: The block hash at which to query.
4361
+ reuse_block: Whether to reuse the last-used block hash.
4362
+
4363
+ Returns:
4364
+ List of (hotkey_ss58, LockState) tuples.
4365
+ """
4366
+ query_map = await self.query_map_subtensor(
4367
+ "Lock",
4368
+ params=[coldkey_ss58, netuid],
4369
+ block=block,
4370
+ block_hash=block_hash,
4371
+ reuse_block=reuse_block,
4372
+ )
4373
+ locks = []
4374
+ async for hotkey, lock_state in query_map:
4375
+ locks.append(
4376
+ (
4377
+ hotkey,
4378
+ LockState(
4379
+ locked_mass=Balance.from_rao(lock_state["locked_mass"], netuid),
4380
+ conviction=fixed_to_float(lock_state["conviction"]),
4381
+ last_update=int(lock_state["last_update"]),
4382
+ ),
4383
+ )
4384
+ )
4385
+ return locks
4386
+
4197
4387
  async def get_stake_movement_fee(
4198
4388
  self,
4199
4389
  origin_netuid: int,
@@ -5280,6 +5470,40 @@ class AsyncSubtensor(SubtensorMixin):
5280
5470
  is not None
5281
5471
  )
5282
5472
 
5473
+ async def is_perpetual_lock(
5474
+ self,
5475
+ coldkey_ss58: str,
5476
+ netuid: int,
5477
+ block: Optional[int] = None,
5478
+ block_hash: Optional[str] = None,
5479
+ reuse_block: bool = False,
5480
+ ) -> bool:
5481
+ """
5482
+ Checks whether a coldkey's lock on a subnet is perpetual (non-decaying).
5483
+
5484
+ Locks decay by default. A lock becomes perpetual only when the coldkey explicitly opts in via
5485
+ set_perpetual_lock(enabled=True), which inserts a ``DecayingLock`` entry with value ``false``. When the entry is
5486
+ absent, the lock decays normally.
5487
+
5488
+ Parameters:
5489
+ coldkey_ss58: The SS58 address of the coldkey to check.
5490
+ netuid: The subnet UID to check.
5491
+ block: The block number to query. Do not specify if using block_hash or reuse_block.
5492
+ block_hash: The block hash at which to query.
5493
+ reuse_block: Whether to reuse the last-used block hash.
5494
+
5495
+ Returns:
5496
+ True if the lock is perpetual (does not decay), False if it decays or no lock exists.
5497
+ """
5498
+ query = await self.query_subtensor(
5499
+ name="DecayingLock",
5500
+ params=[coldkey_ss58, netuid],
5501
+ block=block,
5502
+ block_hash=block_hash,
5503
+ reuse_block=reuse_block,
5504
+ )
5505
+ return query.value is False
5506
+
5283
5507
  async def is_subnet_active(
5284
5508
  self,
5285
5509
  netuid: int,
@@ -7332,6 +7556,53 @@ class AsyncSubtensor(SubtensorMixin):
7332
7556
  wait_for_revealed_execution=wait_for_revealed_execution,
7333
7557
  )
7334
7558
 
7559
+ async def lock_stake(
7560
+ self,
7561
+ wallet: "Wallet",
7562
+ hotkey_ss58: str,
7563
+ netuid: int,
7564
+ amount: Balance,
7565
+ *,
7566
+ mev_protection: bool = DEFAULT_MEV_PROTECTION,
7567
+ period: Optional[int] = DEFAULT_PERIOD,
7568
+ raise_error: bool = False,
7569
+ wait_for_inclusion: bool = True,
7570
+ wait_for_finalization: bool = True,
7571
+ wait_for_revealed_execution: bool = True,
7572
+ ) -> ExtrinsicResponse:
7573
+ """
7574
+ Locks alpha stake on a hotkey within a subnet, building conviction over time.
7575
+
7576
+ Parameters:
7577
+ wallet: The wallet whose coldkey owns the stake to lock.
7578
+ hotkey_ss58: The SS58 address of the hotkey to lock stake on.
7579
+ netuid: The subnet UID on which to lock.
7580
+ amount: Amount of alpha to lock as a Balance object.
7581
+ mev_protection: If `True`, encrypts and submits the transaction through MEV Shield.
7582
+ period: The number of blocks during which the transaction will remain valid after it's submitted.
7583
+ raise_error: Raises exception rather than returning failure response.
7584
+ wait_for_inclusion: Waits for the transaction to be included in a block.
7585
+ wait_for_finalization: Waits for the transaction to be finalized on the blockchain.
7586
+ wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used.
7587
+
7588
+ Returns:
7589
+ ExtrinsicResponse: The result object of the extrinsic execution.
7590
+ """
7591
+ check_balance_amount(amount)
7592
+ return await lock_stake_extrinsic(
7593
+ subtensor=self,
7594
+ wallet=wallet,
7595
+ hotkey_ss58=hotkey_ss58,
7596
+ netuid=netuid,
7597
+ amount=amount,
7598
+ mev_protection=mev_protection,
7599
+ period=period,
7600
+ raise_error=raise_error,
7601
+ wait_for_inclusion=wait_for_inclusion,
7602
+ wait_for_finalization=wait_for_finalization,
7603
+ wait_for_revealed_execution=wait_for_revealed_execution,
7604
+ )
7605
+
7335
7606
  async def mev_submit_encrypted(
7336
7607
  self,
7337
7608
  wallet: "Wallet",
@@ -7481,6 +7752,49 @@ class AsyncSubtensor(SubtensorMixin):
7481
7752
  wait_for_revealed_execution=wait_for_revealed_execution,
7482
7753
  )
7483
7754
 
7755
+ async def move_lock(
7756
+ self,
7757
+ wallet: "Wallet",
7758
+ destination_hotkey_ss58: str,
7759
+ netuid: int,
7760
+ *,
7761
+ mev_protection: bool = DEFAULT_MEV_PROTECTION,
7762
+ period: Optional[int] = DEFAULT_PERIOD,
7763
+ raise_error: bool = False,
7764
+ wait_for_inclusion: bool = True,
7765
+ wait_for_finalization: bool = True,
7766
+ wait_for_revealed_execution: bool = True,
7767
+ ) -> ExtrinsicResponse:
7768
+ """
7769
+ Moves an existing lock from its current hotkey to a different hotkey on the same subnet.
7770
+
7771
+ Parameters:
7772
+ wallet: The wallet whose coldkey owns the lock.
7773
+ destination_hotkey_ss58: The SS58 address of the hotkey to move the lock to.
7774
+ netuid: The subnet UID on which the lock exists.
7775
+ mev_protection: If `True`, encrypts and submits the transaction through MEV Shield.
7776
+ period: The number of blocks during which the transaction will remain valid after it's submitted.
7777
+ raise_error: Raises exception rather than returning failure response.
7778
+ wait_for_inclusion: Waits for the transaction to be included in a block.
7779
+ wait_for_finalization: Waits for the transaction to be finalized on the blockchain.
7780
+ wait_for_revealed_execution: Whether to wait for the revealed execution of transaction if mev_protection used.
7781
+
7782
+ Returns:
7783
+ ExtrinsicResponse: The result object of the extrinsic execution.
7784
+ """
7785
+ return await move_lock_extrinsic(
7786
+ subtensor=self,
7787
+ wallet=wallet,
7788
+ destination_hotkey_ss58=destination_hotkey_ss58,
7789
+ netuid=netuid,
7790
+ mev_protection=mev_protection,
7791
+ period=period,
7792
+ raise_error=raise_error,
7793
+ wait_for_inclusion=wait_for_inclusion,
7794
+ wait_for_finalization=wait_for_finalization,
7795
+ wait_for_revealed_execution=wait_for_revealed_execution,
7796
+ )
7797
+
7484
7798
  async def move_stake(
7485
7799
  self,
7486
7800
  wallet: "Wallet",
@@ -8602,6 +8916,45 @@ class AsyncSubtensor(SubtensorMixin):
8602
8916
  logging.error(f"[red]{response.message}[/red]")
8603
8917
  return response
8604
8918
 
8919
+ async def set_perpetual_lock(
8920
+ self,
8921
+ wallet: "Wallet",
8922
+ netuid: int,
8923
+ enabled: bool,
8924
+ *,
8925
+ period: Optional[int] = DEFAULT_PERIOD,
8926
+ raise_error: bool = False,
8927
+ wait_for_inclusion: bool = True,
8928
+ wait_for_finalization: bool = True,
8929
+ ) -> ExtrinsicResponse:
8930
+ """
8931
+ Sets or clears the perpetual lock flag for the caller's lock on a subnet.
8932
+
8933
+ When enabled, the lock does not decay over time. When disabled, normal decay resumes.
8934
+
8935
+ Parameters:
8936
+ wallet: The wallet whose coldkey owns the lock.
8937
+ netuid: The subnet UID for which to set the perpetual lock flag.
8938
+ enabled: If True, the lock will not decay. If False, normal decay resumes.
8939
+ period: The number of blocks during which the transaction will remain valid after it's submitted.
8940
+ raise_error: Raises exception rather than returning failure response.
8941
+ wait_for_inclusion: Waits for the transaction to be included in a block.
8942
+ wait_for_finalization: Waits for the transaction to be finalized on the blockchain.
8943
+
8944
+ Returns:
8945
+ ExtrinsicResponse: The result object of the extrinsic execution.
8946
+ """
8947
+ return await set_perpetual_lock_extrinsic(
8948
+ subtensor=self,
8949
+ wallet=wallet,
8950
+ netuid=netuid,
8951
+ enabled=enabled,
8952
+ period=period,
8953
+ raise_error=raise_error,
8954
+ wait_for_inclusion=wait_for_inclusion,
8955
+ wait_for_finalization=wait_for_finalization,
8956
+ )
8957
+
8605
8958
  async def set_root_claim_type(
8606
8959
  self,
8607
8960
  wallet: "Wallet",
@@ -285,6 +285,8 @@ _CUSTOM_ERROR_CODE_TO_EXCEPTION: dict[int, type["ChainError"]] = {
285
285
  4: HotKeyAccountNotExists,
286
286
  6: TxRateLimitExceeded,
287
287
  25: NonAssociatedColdKey,
288
+ 26: DelegateTakeTooLow,
289
+ 27: DelegateTakeTooHigh,
288
290
  }
289
291
 
290
292
 
@@ -0,0 +1,238 @@
1
+ from typing import Optional, TYPE_CHECKING
2
+
3
+ from bittensor.core.extrinsics.asyncex.mev_shield import submit_encrypted_extrinsic
4
+ from bittensor.core.extrinsics.pallets import SubtensorModule
5
+ from bittensor.core.settings import DEFAULT_MEV_PROTECTION
6
+ from bittensor.core.types import ExtrinsicResponse
7
+ from bittensor.utils.balance import Balance
8
+ from bittensor.utils.btlogging import logging
9
+
10
+ if TYPE_CHECKING:
11
+ from bittensor_wallet import Wallet
12
+ from bittensor.core.async_subtensor import AsyncSubtensor
13
+
14
+
15
+ async def lock_stake_extrinsic(
16
+ subtensor: "AsyncSubtensor",
17
+ wallet: "Wallet",
18
+ hotkey_ss58: str,
19
+ netuid: int,
20
+ amount: Balance,
21
+ *,
22
+ mev_protection: bool = DEFAULT_MEV_PROTECTION,
23
+ period: Optional[int] = None,
24
+ raise_error: bool = False,
25
+ wait_for_inclusion: bool = True,
26
+ wait_for_finalization: bool = True,
27
+ wait_for_revealed_execution: bool = True,
28
+ ) -> ExtrinsicResponse:
29
+ """
30
+ Locks alpha stake on a hotkey within a subnet, building conviction over time.
31
+
32
+ Parameters:
33
+ subtensor: AsyncSubtensor instance.
34
+ wallet: The wallet whose coldkey owns the stake to lock.
35
+ hotkey_ss58: The SS58 address of the hotkey to lock stake on.
36
+ netuid: The subnet UID on which to lock.
37
+ amount: Amount of alpha to lock.
38
+ mev_protection: If True, encrypts and submits the transaction through MEV Shield.
39
+ period: Number of blocks during which the transaction remains valid.
40
+ raise_error: Raises exception rather than returning failure response.
41
+ wait_for_inclusion: Whether to wait for inclusion in a block.
42
+ wait_for_finalization: Whether to wait for finalization.
43
+ wait_for_revealed_execution: Whether to wait for revealed execution if mev_protection used.
44
+
45
+ Returns:
46
+ ExtrinsicResponse: The result object of the extrinsic execution.
47
+ """
48
+ try:
49
+ if not (
50
+ unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error)
51
+ ).success:
52
+ return unlocked
53
+
54
+ logging.debug(
55
+ f"Locking stake on hotkey [blue]{hotkey_ss58}[/blue] "
56
+ f"on subnet [yellow]{netuid}[/yellow], amount: [green]{amount}[/green]"
57
+ )
58
+
59
+ call = await SubtensorModule(subtensor).lock_stake(
60
+ hotkey=hotkey_ss58,
61
+ netuid=netuid,
62
+ amount=amount.rao,
63
+ )
64
+
65
+ if mev_protection:
66
+ response = await submit_encrypted_extrinsic(
67
+ subtensor=subtensor,
68
+ wallet=wallet,
69
+ call=call,
70
+ period=period,
71
+ raise_error=raise_error,
72
+ wait_for_inclusion=wait_for_inclusion,
73
+ wait_for_finalization=wait_for_finalization,
74
+ wait_for_revealed_execution=wait_for_revealed_execution,
75
+ )
76
+ else:
77
+ response = await subtensor.sign_and_send_extrinsic(
78
+ call=call,
79
+ wallet=wallet,
80
+ wait_for_inclusion=wait_for_inclusion,
81
+ wait_for_finalization=wait_for_finalization,
82
+ period=period,
83
+ raise_error=raise_error,
84
+ )
85
+
86
+ if response.success:
87
+ logging.debug("[green]Lock stake finalized[/green]")
88
+ else:
89
+ logging.error(f"[red]{response.message}[/red]")
90
+
91
+ return response
92
+
93
+ except Exception as error:
94
+ return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error)
95
+
96
+
97
+ async def move_lock_extrinsic(
98
+ subtensor: "AsyncSubtensor",
99
+ wallet: "Wallet",
100
+ destination_hotkey_ss58: str,
101
+ netuid: int,
102
+ *,
103
+ mev_protection: bool = DEFAULT_MEV_PROTECTION,
104
+ period: Optional[int] = None,
105
+ raise_error: bool = False,
106
+ wait_for_inclusion: bool = True,
107
+ wait_for_finalization: bool = True,
108
+ wait_for_revealed_execution: bool = True,
109
+ ) -> ExtrinsicResponse:
110
+ """
111
+ Moves an existing lock from its current hotkey to a different hotkey on the same subnet.
112
+
113
+ Parameters:
114
+ subtensor: AsyncSubtensor instance.
115
+ wallet: The wallet whose coldkey owns the lock.
116
+ destination_hotkey_ss58: The SS58 address of the hotkey to move the lock to.
117
+ netuid: The subnet UID on which the lock exists.
118
+ mev_protection: If True, encrypts and submits the transaction through MEV Shield.
119
+ period: Number of blocks during which the transaction remains valid.
120
+ raise_error: Raises exception rather than returning failure response.
121
+ wait_for_inclusion: Whether to wait for inclusion in a block.
122
+ wait_for_finalization: Whether to wait for finalization.
123
+ wait_for_revealed_execution: Whether to wait for revealed execution if mev_protection used.
124
+
125
+ Returns:
126
+ ExtrinsicResponse: The result object of the extrinsic execution.
127
+ """
128
+ try:
129
+ if not (
130
+ unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error)
131
+ ).success:
132
+ return unlocked
133
+
134
+ logging.debug(
135
+ f"Moving lock to hotkey [blue]{destination_hotkey_ss58}[/blue] "
136
+ f"on subnet [yellow]{netuid}[/yellow]"
137
+ )
138
+
139
+ call = await SubtensorModule(subtensor).move_lock(
140
+ destination_hotkey=destination_hotkey_ss58,
141
+ netuid=netuid,
142
+ )
143
+
144
+ if mev_protection:
145
+ response = await submit_encrypted_extrinsic(
146
+ subtensor=subtensor,
147
+ wallet=wallet,
148
+ call=call,
149
+ period=period,
150
+ raise_error=raise_error,
151
+ wait_for_inclusion=wait_for_inclusion,
152
+ wait_for_finalization=wait_for_finalization,
153
+ wait_for_revealed_execution=wait_for_revealed_execution,
154
+ )
155
+ else:
156
+ response = await subtensor.sign_and_send_extrinsic(
157
+ call=call,
158
+ wallet=wallet,
159
+ wait_for_inclusion=wait_for_inclusion,
160
+ wait_for_finalization=wait_for_finalization,
161
+ period=period,
162
+ raise_error=raise_error,
163
+ )
164
+
165
+ if response.success:
166
+ logging.debug("[green]Move lock finalized[/green]")
167
+ else:
168
+ logging.error(f"[red]{response.message}[/red]")
169
+
170
+ return response
171
+
172
+ except Exception as error:
173
+ return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error)
174
+
175
+
176
+ async def set_perpetual_lock_extrinsic(
177
+ subtensor: "AsyncSubtensor",
178
+ wallet: "Wallet",
179
+ netuid: int,
180
+ enabled: bool,
181
+ *,
182
+ period: Optional[int] = None,
183
+ raise_error: bool = False,
184
+ wait_for_inclusion: bool = True,
185
+ wait_for_finalization: bool = True,
186
+ ) -> ExtrinsicResponse:
187
+ """
188
+ Sets or clears the perpetual lock flag for the caller's lock on a subnet.
189
+
190
+ When enabled, the lock does not decay over time. When disabled, normal decay resumes.
191
+
192
+ Parameters:
193
+ subtensor: AsyncSubtensor instance.
194
+ wallet: The wallet whose coldkey owns the lock.
195
+ netuid: The subnet UID for which to set the perpetual lock flag.
196
+ enabled: If True, the lock will not decay. If False, normal decay resumes.
197
+ period: Number of blocks during which the transaction remains valid.
198
+ raise_error: Raises exception rather than returning failure response.
199
+ wait_for_inclusion: Whether to wait for inclusion in a block.
200
+ wait_for_finalization: Whether to wait for finalization.
201
+
202
+ Returns:
203
+ ExtrinsicResponse: The result object of the extrinsic execution.
204
+ """
205
+ try:
206
+ if not (
207
+ unlocked := ExtrinsicResponse.unlock_wallet(wallet, raise_error)
208
+ ).success:
209
+ return unlocked
210
+
211
+ logging.debug(
212
+ f"Setting perpetual lock to [green]{enabled}[/green] "
213
+ f"on subnet [yellow]{netuid}[/yellow]"
214
+ )
215
+
216
+ call = await SubtensorModule(subtensor).set_perpetual_lock(
217
+ netuid=netuid,
218
+ enabled=enabled,
219
+ )
220
+
221
+ response = await subtensor.sign_and_send_extrinsic(
222
+ call=call,
223
+ wallet=wallet,
224
+ wait_for_inclusion=wait_for_inclusion,
225
+ wait_for_finalization=wait_for_finalization,
226
+ period=period,
227
+ raise_error=raise_error,
228
+ )
229
+
230
+ if response.success:
231
+ logging.debug("[green]Set perpetual lock finalized[/green]")
232
+ else:
233
+ logging.error(f"[red]{response.message}[/red]")
234
+
235
+ return response
236
+
237
+ except Exception as error:
238
+ return ExtrinsicResponse.from_exception(raise_error=raise_error, error=error)