Skip to content
54 changes: 15 additions & 39 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4191,54 +4191,30 @@ async def get_stake(
reuse_block: bool = False,
) -> Balance:
"""
Returns the stake under a coldkey - hotkey pairing.
Returns the amount of Alpha staked by a specific coldkey to a specific hotkey within a given subnet.

Parameters:
hotkey_ss58: The SS58 address of the hotkey.
coldkey_ss58: The SS58 address of the coldkey.
netuid: The subnet ID.
block: The block number at which to query the stake information.
block_hash: The hash of the block to retrieve the stake from. Do not specify if using block
or reuse_block
reuse_block: Whether to use the last-used block. Do not set if using `block_hash` or `block`.
coldkey_ss58: The SS58 address of the coldkey that delegated the stake. This address owns the stake.
hotkey_ss58: The SS58 address of the hotkey which the stake is on.
netuid: The unique identifier of the subnet to query.
block: The specific block number at which to retrieve the stake information.
or `reuse_block`.
block_hash: The hash of the block to retrieve the stake from. Do not specify if using `block`
or `reuse_block`.
reuse_block: Whether to use the last-used block hash. Do not set if using `block_hash` or `block`.

Returns:
Balance: The stake under the coldkey - hotkey pairing.
An object representing the amount of Alpha (TAO ONLY if the subnet's netuid is 0) currently staked from the
specified coldkey to the specified hotkey within the given subnet.
"""
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)

alpha_shares = await self.query_subtensor(
name="Alpha",
block=block,
block_hash=block_hash,
reuse_block=reuse_block,
result = await self.query_runtime_api(
runtime_api="StakeInfoRuntimeApi",
method="get_stake_info_for_hotkey_coldkey_netuid",
params=[hotkey_ss58, coldkey_ss58, netuid],
)
hotkey_alpha_result = await self.query_subtensor(
name="TotalHotkeyAlpha",
block=block,
block_hash=block_hash,
reuse_block=reuse_block,
params=[hotkey_ss58, netuid],
)
hotkey_shares = await self.query_subtensor(
name="TotalHotkeyShares",
block=block,
block_hash=block_hash,
reuse_block=reuse_block,
params=[hotkey_ss58, netuid],
)

hotkey_alpha = getattr(hotkey_alpha_result, "value", hotkey_alpha_result)
alpha_shares_as_float = fixed_to_float(alpha_shares)
hotkey_shares_as_float = fixed_to_float(hotkey_shares)

if hotkey_shares_as_float == 0:
return Balance.from_rao(0).set_unit(netuid=netuid)

stake = alpha_shares_as_float / hotkey_shares_as_float * cast(int, hotkey_alpha)

return Balance.from_rao(int(stake)).set_unit(netuid=netuid)
return StakeInfo.from_dict(result).stake

async def get_stake_add_fee(
self,
Expand Down
6 changes: 4 additions & 2 deletions bittensor/core/extrinsics/asyncex/mev_shield.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from bittensor.core.extrinsics.utils import (
get_mev_shielded_ciphertext,
get_event_data_by_event_name,
resolve_mev_shield_period,
)
from bittensor.core.types import ExtrinsicResponse
from bittensor.utils import format_error_message
Expand Down Expand Up @@ -146,7 +147,8 @@ async def submit_encrypted_extrinsic(

inner_signing_keypair = getattr(wallet, sign_with)

era = "00" if period is None else {"period": period}
effective_period = resolve_mev_shield_period(period)
era = {"period": effective_period}

current_nonce = await subtensor.substrate.get_account_next_index(
account_address=inner_signing_keypair.ss58_address
Expand All @@ -172,7 +174,7 @@ async def submit_encrypted_extrinsic(
sign_with=sign_with,
call=extrinsic_call,
nonce=current_nonce,
period=period,
period=effective_period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
Expand Down
6 changes: 4 additions & 2 deletions bittensor/core/extrinsics/mev_shield.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from bittensor.core.extrinsics.utils import (
get_mev_shielded_ciphertext,
get_event_data_by_event_name,
resolve_mev_shield_period,
)
from bittensor.core.types import ExtrinsicResponse
from bittensor.utils.btlogging import logging
Expand Down Expand Up @@ -146,7 +147,8 @@ def submit_encrypted_extrinsic(

inner_signing_keypair = getattr(wallet, sign_with)

era = "00" if period is None else {"period": period}
effective_period = resolve_mev_shield_period(period)
era = {"period": effective_period}

current_nonce = subtensor.substrate.get_account_next_index(
account_address=inner_signing_keypair.ss58_address
Expand All @@ -170,7 +172,7 @@ def submit_encrypted_extrinsic(
sign_with=sign_with,
call=extrinsic_call,
nonce=current_nonce,
period=period,
period=effective_period,
raise_error=raise_error,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
Expand Down
28 changes: 23 additions & 5 deletions bittensor/core/extrinsics/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,39 @@
from bittensor_wallet import Keypair

from bittensor.core.extrinsics.pallets import Sudo
from bittensor.core.settings import MAX_MEV_SHIELD_PERIOD
from bittensor.core.types import ExtrinsicResponse
from bittensor.utils.balance import Balance

if TYPE_CHECKING:
from bittensor_wallet import Wallet
from bittensor.core.chain_data import StakeInfo
from bittensor.core.subtensor import Subtensor
from scalecodec.types import GenericExtrinsic

# TODO: Michael/Roman add the link to the docs once it's ready.'
MEV_HOTKEY_USAGE_WARNING = (
"MeV Shield cannot be used with hotkey-signed extrinsics. The transaction will fail because the hotkey cannot pay "
"the required extrinsic deposit fee. If you still want to use the hot key, please top up your balance before making"
" the transaction."
)

if TYPE_CHECKING:
from bittensor_wallet import Wallet
from bittensor.core.chain_data import StakeInfo
from bittensor.core.subtensor import Subtensor
from scalecodec.types import GenericExtrinsic

def resolve_mev_shield_period(period: Optional[int]) -> int:
"""Return effective era period for MEV Shield extrinsics.

MEV Shield extrinsics must use a short-lived era. If period is omitted or
exceeds the MEV limit, the maximum allowed MEV period is applied.

Parameters:
period: The period to resolve.

Returns:
The effective period (in blocks).
"""
if period is None or period > MAX_MEV_SHIELD_PERIOD:
return MAX_MEV_SHIELD_PERIOD
return period


def get_old_stakes(
Expand Down
4 changes: 4 additions & 0 deletions bittensor/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
# details https://paritytech.github.io/polkadot-sdk/master/src/sp_runtime/generic/era.rs.html#65-72
DEFAULT_PERIOD = 128

# Maximum period (in blocks) for MEV Shield-protected extrinsics Era.
# This keeps encrypted submissions short-lived in the mempool.
MAX_MEV_SHIELD_PERIOD = 8

# Default MEV Shield protection setting for extrinsics.
# When enabled, transactions are encrypted to protect against Miner Extractable Value (MEV) attacks.
DEFAULT_MEV_PROTECTION = os.getenv("BT_MEV_PROTECTION", "").lower() in (
Expand Down
39 changes: 5 additions & 34 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3465,53 +3465,24 @@ def get_stake(
) -> Balance:
"""
Returns the amount of Alpha staked by a specific coldkey to a specific hotkey within a given subnet.
This function retrieves the delegated stake balance, referred to as the 'Alpha' value.

Parameters:
coldkey_ss58: The SS58 address of the coldkey that delegated the stake. This address owns the stake.
hotkey_ss58: The ss58 address of the hotkey which the stake is on.
hotkey_ss58: The SS58 address of the hotkey which the stake is on.
netuid: The unique identifier of the subnet to query.
block: The specific block number at which to retrieve the stake information.

Returns:
An object representing the amount of Alpha (TAO ONLY if the subnet's netuid is 0) currently staked from the
specified coldkey to the specified hotkey within the given subnet.
"""
alpha_shares_query = self.query_module(
module="SubtensorModule",
name="Alpha",
block=block,
result = self.query_runtime_api(
runtime_api="StakeInfoRuntimeApi",
method="get_stake_info_for_hotkey_coldkey_netuid",
params=[hotkey_ss58, coldkey_ss58, netuid],
)
alpha_shares = alpha_shares_query

hotkey_alpha_obj = cast(
ScaleObj,
self.query_module(
module="SubtensorModule",
name="TotalHotkeyAlpha",
block=block,
params=[hotkey_ss58, netuid],
),
)
hotkey_alpha = hotkey_alpha_obj.value

hotkey_shares = self.query_module(
module="SubtensorModule",
name="TotalHotkeyShares",
block=block,
params=[hotkey_ss58, netuid],
)

alpha_shares_as_float = fixed_to_float(alpha_shares)
hotkey_shares_as_float = fixed_to_float(hotkey_shares)

if hotkey_shares_as_float == 0:
return Balance.from_rao(0).set_unit(netuid=netuid)

stake = alpha_shares_as_float / hotkey_shares_as_float * hotkey_alpha

return Balance.from_rao(int(stake)).set_unit(netuid=netuid)
return StakeInfo.from_dict(result).stake

def get_stake_for_coldkey_and_hotkey(
self,
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e_tests/test_root_claim.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ def test_root_claim_keep_with_zero_num_root_auto_claims(
hotkey_ss58=alice_wallet.hotkey.ss58_address,
netuid=sn2.netuid,
)
assert stake_after_charlie >= claimable_stake_before_charlie
assert stake_after_charlie >= claimable_stake_before_charlie - Balance.from_rao(1, sn2.netuid)

logging.console.info(f"[blue]Charlie after:[/blue]")
logging.console.info(f"RootClaimed: {claimed_after_charlie}")
Expand Down Expand Up @@ -559,7 +559,7 @@ async def test_root_claim_keep_with_zero_num_root_auto_claims_async(
hotkey_ss58=alice_wallet.hotkey.ss58_address,
netuid=sn2.netuid,
)
assert stake_after_charlie >= claimable_stake_before_charlie
assert stake_after_charlie >= claimable_stake_before_charlie - Balance.from_rao(1, sn2.netuid)

logging.console.info(f"[blue]Charlie after:[/blue]")
logging.console.info(f"RootClaimed: {claimed_after_charlie}")
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e_tests/test_staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,8 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet):
)

assert CloseInValue( # Make sure we are within 0.0002 TAO due to tx fees
balances[bob_wallet.coldkey.ss58_address], Balance.from_rao(1_500_000)
) == Balance.from_tao(999_999.7956)
balances[bob_wallet.coldkey.ss58_address], Balance.from_rao(5_000_000)
) == Balance.from_tao(999_999.7979)

assert balances[alice_wallet.coldkey.ss58_address] > alice_balance

Expand Down Expand Up @@ -570,8 +570,8 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet)
)

assert CloseInValue( # Make sure we are within 0.0002 TAO due to tx fees
balances[bob_wallet.coldkey.ss58_address], Balance.from_rao(1_500_000)
) == Balance.from_tao(999_999.7956)
balances[bob_wallet.coldkey.ss58_address], Balance.from_rao(5_000_000)
) == Balance.from_tao(999_999.7979)

assert balances[alice_wallet.coldkey.ss58_address] > alice_balance

Expand Down
Loading
Loading