Skip to content

Commit c9dc180

Browse files
authored
Merge pull request #3117 from opentensor/feat/roman/root-claim-airdrop
[v10] `Root claim/ Airdrop` mechanism implementation
2 parents 19ba3d0 + fbfb0e0 commit c9dc180

File tree

19 files changed

+1744
-144
lines changed

19 files changed

+1744
-144
lines changed

bittensor/core/async_subtensor.py

Lines changed: 270 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import copy
33
import ssl
44
from datetime import datetime, timezone
5-
from typing import cast, Optional, Any, Union, Iterable, TYPE_CHECKING
5+
from typing import cast, Optional, Any, Union, Iterable, TYPE_CHECKING, Literal
66

77
import asyncstdlib as a
88
import scalecodec
@@ -73,7 +73,11 @@
7373
register_subnet_extrinsic,
7474
set_subnet_identity_extrinsic,
7575
)
76-
from bittensor.core.extrinsics.asyncex.root import root_register_extrinsic
76+
from bittensor.core.extrinsics.asyncex.root import (
77+
claim_root_extrinsic,
78+
root_register_extrinsic,
79+
set_root_claim_type_extrinsic,
80+
)
7781
from bittensor.core.extrinsics.asyncex.serving import (
7882
publish_metadata_extrinsic,
7983
)
@@ -2424,6 +2428,17 @@ async def get_liquidity_list(
24242428
logging.debug(f"Subnet {netuid} is not active.")
24252429
return None
24262430

2431+
positions_response = await self.query_map(
2432+
module="Swap",
2433+
name="Positions",
2434+
params=[netuid, wallet.coldkeypub.ss58_address],
2435+
block=block,
2436+
block_hash=block_hash,
2437+
reuse_block=reuse_block,
2438+
)
2439+
if len(positions_response.records) == 0:
2440+
return []
2441+
24272442
block_hash = await self.determine_block_hash(
24282443
block=block, block_hash=block_hash, reuse_block=reuse_block
24292444
)
@@ -2447,25 +2462,20 @@ async def get_liquidity_list(
24472462
params=[netuid],
24482463
block_hash=block_hash,
24492464
)
2465+
24502466
(
2451-
(fee_global_tao_query, fee_global_alpha_query, sqrt_price_query),
2452-
positions_response,
2453-
) = await asyncio.gather(
2454-
self.substrate.query_multi(
2455-
[
2456-
fee_global_tao_query_sk,
2457-
fee_global_alpha_query_sk,
2458-
sqrt_price_query_sk,
2459-
],
2460-
block_hash=block_hash,
2461-
),
2462-
self.query_map(
2463-
module="Swap",
2464-
name="Positions",
2465-
block=block,
2466-
params=[netuid, wallet.coldkeypub.ss58_address],
2467-
),
2467+
fee_global_tao_query,
2468+
fee_global_alpha_query,
2469+
sqrt_price_query,
2470+
) = await self.substrate.query_multi(
2471+
storage_keys=[
2472+
fee_global_tao_query_sk,
2473+
fee_global_alpha_query_sk,
2474+
sqrt_price_query_sk,
2475+
],
2476+
block_hash=block_hash,
24682477
)
2478+
24692479
# convert to floats
24702480
fee_global_tao = fixed_to_float(fee_global_tao_query[1])
24712481
fee_global_alpha = fixed_to_float(fee_global_alpha_query[1])
@@ -3029,6 +3039,179 @@ async def get_revealed_commitment_by_hotkey(
30293039
return None
30303040
return tuple(decode_revealed_commitment(pair) for pair in query)
30313041

3042+
async def get_root_claim_type(
3043+
self,
3044+
coldkey_ss58: str,
3045+
block: Optional[int] = None,
3046+
block_hash: Optional[str] = None,
3047+
reuse_block: bool = False,
3048+
) -> str:
3049+
"""Retrieves the root claim type for a given coldkey address.
3050+
3051+
Parameters:
3052+
coldkey_ss58: The ss58 address of the coldkey.
3053+
block: The block number to query. Do not specify if using block_hash or reuse_block.
3054+
block_hash: The block hash at which to check the parameter. Do not set if using block or reuse_block.
3055+
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
3056+
3057+
Returns:
3058+
RootClaimType value in string representation. Could be `Swap` or `Keep`.
3059+
"""
3060+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3061+
query = await self.substrate.query(
3062+
module="SubtensorModule",
3063+
storage_function="RootClaimType",
3064+
params=[coldkey_ss58],
3065+
block_hash=block_hash,
3066+
reuse_block_hash=reuse_block,
3067+
)
3068+
return next(iter(query.keys()))
3069+
3070+
async def get_root_claimable_rate(
3071+
self,
3072+
hotkey_ss58: str,
3073+
netuid: int,
3074+
block: Optional[int] = None,
3075+
block_hash: Optional[str] = None,
3076+
reuse_block: bool = False,
3077+
) -> float:
3078+
"""Retrieves the root claimable rate from a given hotkey address for provided netuid.
3079+
3080+
Parameters:
3081+
hotkey_ss58: The ss58 address of the root validator.
3082+
netuid: The unique identifier of the subnet to get the rate.
3083+
block: The block number to query. Do not specify if using block_hash or reuse_block.
3084+
block_hash: The block hash at which to check the parameter. Do not set if using block or reuse_block.
3085+
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
3086+
3087+
Returns:
3088+
The rate of claimable stake from validator's hotkey ss58 address for provided subnet.
3089+
"""
3090+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3091+
all_rates = await self.get_root_claimable_all_rates(
3092+
hotkey_ss58=hotkey_ss58,
3093+
block=block_hash,
3094+
)
3095+
return all_rates.get(netuid, 0.0)
3096+
3097+
async def get_root_claimable_all_rates(
3098+
self,
3099+
hotkey_ss58: str,
3100+
block: Optional[int] = None,
3101+
block_hash: Optional[str] = None,
3102+
reuse_block: bool = False,
3103+
) -> dict[int, float]:
3104+
"""Retrieves all root claimable rates from a given hotkey address for all subnets with this validator.
3105+
3106+
Parameters:
3107+
hotkey_ss58: The ss58 address of the root validator.
3108+
block: The block number to query. Do not specify if using block_hash or reuse_block.
3109+
block_hash: The block hash at which to check the parameter. Do not set if using block or reuse_block.
3110+
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
3111+
3112+
Returns:
3113+
The rate of claimable stake from validator's hotkey ss58 address for provided subnet.
3114+
"""
3115+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3116+
query = await self.substrate.query(
3117+
module="SubtensorModule",
3118+
storage_function="RootClaimable",
3119+
params=[hotkey_ss58],
3120+
block_hash=block_hash,
3121+
reuse_block_hash=reuse_block,
3122+
)
3123+
bits_list = next(iter(query.value))
3124+
return {bits[0]: fixed_to_float(bits[1], frac_bits=32) for bits in bits_list}
3125+
3126+
async def get_root_claimable_stake(
3127+
self,
3128+
coldkey_ss58: str,
3129+
hotkey_ss58: str,
3130+
netuid: int,
3131+
block: Optional[int] = None,
3132+
block_hash: Optional[str] = None,
3133+
reuse_block: bool = False,
3134+
) -> Balance:
3135+
"""
3136+
Retrieves the root claimable stake for a given coldkey address.
3137+
3138+
Parameters:
3139+
coldkey_ss58: Delegate's ColdKey ss58 address.
3140+
hotkey_ss58: The root validator hotkey ss58 address.
3141+
netuid: Delegate's netuid where stake will be claimed.
3142+
block: The block number to query. Do not specify if using block_hash or reuse_block.
3143+
block_hash: The block hash at which to check the parameter. Do not set if using block or reuse_block.
3144+
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
3145+
3146+
Returns:
3147+
Available for claiming root stake.
3148+
3149+
Note:
3150+
After manual claim, claimable (available) stake will be added to subtends stake.
3151+
"""
3152+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3153+
root_stake = await self.get_stake(
3154+
coldkey_ss58=coldkey_ss58,
3155+
hotkey_ss58=hotkey_ss58,
3156+
netuid=0, # root netuid
3157+
block=block,
3158+
block_hash=block_hash,
3159+
reuse_block=reuse_block,
3160+
)
3161+
root_claimable_rate = await self.get_root_claimable_rate(
3162+
hotkey_ss58=hotkey_ss58,
3163+
netuid=netuid,
3164+
block=block,
3165+
block_hash=block_hash,
3166+
reuse_block=reuse_block,
3167+
)
3168+
root_claimable_stake = (root_claimable_rate * root_stake).set_unit(
3169+
netuid=netuid
3170+
)
3171+
root_claimed = await self.get_root_claimed(
3172+
coldkey_ss58=coldkey_ss58,
3173+
hotkey_ss58=hotkey_ss58,
3174+
netuid=netuid,
3175+
block=block,
3176+
block_hash=block_hash,
3177+
reuse_block=reuse_block,
3178+
)
3179+
return max(
3180+
root_claimable_stake - root_claimed, Balance(0).set_unit(netuid=netuid)
3181+
)
3182+
3183+
async def get_root_claimed(
3184+
self,
3185+
coldkey_ss58: str,
3186+
hotkey_ss58: str,
3187+
netuid: int,
3188+
block: Optional[int] = None,
3189+
block_hash: Optional[str] = None,
3190+
reuse_block: bool = False,
3191+
) -> Balance:
3192+
"""Retrieves the root claimed Alpha shares for coldkey from hotkey in provided subnet.
3193+
3194+
Parameters:
3195+
coldkey_ss58: The ss58 address of the staker.
3196+
hotkey_ss58: The ss58 address of the root validator.
3197+
netuid: The unique identifier of the subnet.
3198+
block: The block number to query. Do not specify if using block_hash or reuse_block.
3199+
block_hash: The block hash at which to check the parameter. Do not set if using block or reuse_block.
3200+
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
3201+
3202+
Returns:
3203+
The number of Alpha stake claimed from the root validator in Rao.
3204+
"""
3205+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
3206+
query = await self.substrate.query(
3207+
module="SubtensorModule",
3208+
storage_function="RootClaimed",
3209+
params=[hotkey_ss58, coldkey_ss58, netuid],
3210+
block_hash=block_hash,
3211+
reuse_block_hash=reuse_block,
3212+
)
3213+
return Balance.from_rao(query.value).set_unit(netuid=netuid)
3214+
30323215
async def get_stake(
30333216
self,
30343217
coldkey_ss58: str,
@@ -5095,6 +5278,40 @@ async def burned_register(
50955278
wait_for_finalization=wait_for_finalization,
50965279
)
50975280

5281+
async def claim_root(
5282+
self,
5283+
wallet: "Wallet",
5284+
netuids: "UIDs",
5285+
period: Optional[int] = DEFAULT_PERIOD,
5286+
raise_error: bool = False,
5287+
wait_for_inclusion: bool = True,
5288+
wait_for_finalization: bool = True,
5289+
):
5290+
"""Claims the root emissions for a coldkey.
5291+
5292+
Parameters:
5293+
wallet: Bittensor Wallet instance.
5294+
netuids: The netuids to claim root emissions for.
5295+
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
5296+
transaction is not included in a block within that number of blocks, it will expire and be rejected. You
5297+
can think of it as an expiration date for the transaction.
5298+
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
5299+
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
5300+
wait_for_finalization: Whether to wait for the finalization of the transaction.
5301+
5302+
Returns:
5303+
ExtrinsicResponse: The result object of the extrinsic execution.
5304+
"""
5305+
return await claim_root_extrinsic(
5306+
subtensor=self,
5307+
wallet=wallet,
5308+
netuids=netuids,
5309+
period=period,
5310+
raise_error=raise_error,
5311+
wait_for_inclusion=wait_for_inclusion,
5312+
wait_for_finalization=wait_for_finalization,
5313+
)
5314+
50985315
async def commit_weights(
50995316
self,
51005317
wallet: "Wallet",
@@ -5967,6 +6184,40 @@ async def set_delegate_take(
59676184
logging.error(f"[red]{response.message}[/red]")
59686185
return response
59696186

6187+
async def set_root_claim_type(
6188+
self,
6189+
wallet: "Wallet",
6190+
new_root_claim_type: Literal["Swap", "Keep"],
6191+
period: Optional[int] = DEFAULT_PERIOD,
6192+
raise_error: bool = False,
6193+
wait_for_inclusion: bool = True,
6194+
wait_for_finalization: bool = True,
6195+
):
6196+
"""Sets the root claim type for the coldkey in provided wallet.
6197+
6198+
Parameters:
6199+
wallet: Bittensor Wallet instance.
6200+
new_root_claim_type: The new root claim type to set. Could be either "Swap" or "Keep".
6201+
period: The number of blocks during which the transaction will remain valid after it's submitted. If the
6202+
transaction is not included in a block within that number of blocks, it will expire and be rejected. You
6203+
can think of it as an expiration date for the transaction.
6204+
raise_error: Raises a relevant exception rather than returning `False` if unsuccessful.
6205+
wait_for_inclusion: Whether to wait for the inclusion of the transaction.
6206+
wait_for_finalization: Whether to wait for the finalization of the transaction.
6207+
6208+
Returns:
6209+
ExtrinsicResponse: The result object of the extrinsic execution.
6210+
"""
6211+
return await set_root_claim_type_extrinsic(
6212+
subtensor=self,
6213+
wallet=wallet,
6214+
new_root_claim_type=new_root_claim_type,
6215+
period=period,
6216+
raise_error=raise_error,
6217+
wait_for_inclusion=wait_for_inclusion,
6218+
wait_for_finalization=wait_for_finalization,
6219+
)
6220+
59706221
async def set_subnet_identity(
59716222
self,
59726223
wallet: "Wallet",

0 commit comments

Comments
 (0)