Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/delegate all #125

Merged
merged 4 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
4 changes: 2 additions & 2 deletions bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2670,7 +2670,7 @@ def root_delegate_stake(
root.delegate_stake(
wallet,
self.initialize_chain(network, chain),
float(amount),
amount,
delegate_ss58key,
prompt,
)
Expand Down Expand Up @@ -2741,7 +2741,7 @@ def root_undelegate_stake(
root.delegate_unstake(
wallet,
self.initialize_chain(network, chain),
float(amount),
amount,
delegate_ss58key,
prompt,
)
Expand Down
39 changes: 21 additions & 18 deletions bittensor_cli/src/commands/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ async def delegate_extrinsic(
subtensor: SubtensorInterface,
wallet: Wallet,
delegate_ss58: str,
amount: Balance,
amount: Optional[float],
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = False,
Expand All @@ -472,7 +472,7 @@ async def delegate_extrinsic(
:param subtensor: The SubtensorInterface used to perform the delegation, initialized.
:param wallet: Bittensor wallet object.
:param delegate_ss58: The `ss58` address of the delegate.
:param amount: Amount to stake as bittensor balance
:param amount: Amount to stake as bittensor balance, None to stake all available TAO.
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
`False` if the extrinsic fails to enter the block within the timeout.
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
Expand All @@ -484,19 +484,19 @@ async def delegate_extrinsic(
the response is `True`.
"""

async def _do_delegation() -> tuple[bool, str]:
async def _do_delegation(staking_balance_: Balance) -> tuple[bool, str]:
"""Performs the delegation extrinsic call to the chain."""
if delegate:
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="add_stake",
call_params={"hotkey": delegate_ss58, "amount_staked": amount.rao},
call_params={"hotkey": delegate_ss58, "amount_staked": staking_balance_.rao},
)
else:
call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="remove_stake",
call_params={"hotkey": delegate_ss58, "amount_unstaked": amount.rao},
call_params={"hotkey": delegate_ss58, "amount_unstaked": staking_balance_.rao},
)
return await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization
Expand Down Expand Up @@ -569,14 +569,7 @@ async def get_stake_for_coldkey_and_hotkey(
# Stake it all.
staking_balance = Balance.from_tao(my_prev_coldkey_balance.tao)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey!

Since this also include undelegating, we should also make a distinction here when unstaking all:

    if amount is None:
        # Stake it all.
        if delegate_string == "delegate":
            staking_balance = Balance.from_tao(my_prev_coldkey_balance.tao)
        else:
            # Unstake all
            staking_balance = Balance.from_tao(my_prev_delegated_stake.tao)
    else:
        staking_balance = Balance.from_tao(amount)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Unit tests need to be updated as well to check for the wallet balance change. Unit tests stopped working locally for me (infinite wait for the local subtensor). Once I resolve the issue I will update the tests too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests updated.

However, I couldn't check the delegate balance when staking all. Float existential deposit, some bug or output value got truncated (rounded). Used wallet balance instead. Open for suggestions or could be just merged as is :)

else:
staking_balance = amount

if delegate:
# Remove existential balance to keep key alive.
if staking_balance > (b1k := Balance.from_rao(1000)):
staking_balance = staking_balance - b1k
else:
staking_balance = staking_balance
staking_balance = Balance.from_tao(amount)

# Check enough balance to stake.
if delegate_string == "delegate" and staking_balance > my_prev_coldkey_balance:
Expand All @@ -599,6 +592,16 @@ async def get_stake_for_coldkey_and_hotkey(
)
return False

if delegate:
# Grab the existential deposit.
existential_deposit = await subtensor.get_existential_deposit()

# Remove existential balance to keep key alive.
if staking_balance > my_prev_coldkey_balance - existential_deposit:
staking_balance = my_prev_coldkey_balance - existential_deposit
else:
staking_balance = staking_balance

# Ask before moving on.
if prompt:
if not Confirm.ask(
Expand All @@ -615,7 +618,7 @@ async def get_stake_for_coldkey_and_hotkey(
spinner="aesthetic",
) as status:
print_verbose("Transmitting delegate operation call")
staking_response, err_msg = await _do_delegation()
staking_response, err_msg = await _do_delegation(staking_balance)

if staking_response is True: # If we successfully staked.
# We only wait here if we expect finalization.
Expand Down Expand Up @@ -1318,7 +1321,7 @@ async def _do_set_take() -> bool:
async def delegate_stake(
wallet: Wallet,
subtensor: SubtensorInterface,
amount: float,
amount: Optional[float],
delegate_ss58key: str,
prompt: bool,
):
Expand All @@ -1328,7 +1331,7 @@ async def delegate_stake(
subtensor,
wallet,
delegate_ss58key,
Balance.from_tao(amount),
amount,
wait_for_inclusion=True,
prompt=prompt,
delegate=True,
Expand All @@ -1338,7 +1341,7 @@ async def delegate_stake(
async def delegate_unstake(
wallet: Wallet,
subtensor: SubtensorInterface,
amount: float,
amount: Optional[float],
delegate_ss58key: str,
prompt: bool,
):
Expand All @@ -1348,7 +1351,7 @@ async def delegate_unstake(
subtensor,
wallet,
delegate_ss58key,
Balance.from_tao(amount),
amount,
wait_for_inclusion=True,
prompt=prompt,
delegate=False,
Expand Down
72 changes: 70 additions & 2 deletions tests/e2e_tests/test_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def test_root_commands(local_chain, wallet_setup):
4. Execute list delegates and verify information
5. Execute set-take command, change the take to 12%, verify
6. Execute delegate-stake command, stake from Alice to Bob
7. Execute undelegate-stake command, unstake from Bob to Alice
8. Execute delegate-stake command, stake all from Alice to Bob
9. Execute undelegate-stake command, unstake all from Bob to Alice

Raises:
AssertionError: If any of the checks or verifications fail
Expand Down Expand Up @@ -219,12 +222,12 @@ def test_root_commands(local_chain, wallet_setup):
delegate_stake = Balance.from_tao(string_tao_to_float(alice_delegates_info[3]))
assert delegate_stake == Balance.from_tao(delegate_amount)

# TOTAL STAKE(τ): This should be 10 as only Alice has delegated to Bob
# TOTAL STAKE(τ): This should be `delegate_amount` as only Alice has delegated to Bob
total_stake = Balance.from_tao(string_tao_to_float(alice_delegates_info[7]))
assert total_stake == Balance.from_tao(delegate_amount)

# Total delegated Tao: This is listed at the bottom of the information
# Since Alice has only delegated to Bob, total should be 10 TAO
# Since Alice has only delegated to Bob, total should be `delegate_amount` TAO
total_delegated_tao = Balance.from_tao(
string_tao_to_float(alice_delegates.stdout.splitlines()[8].split()[3])
)
Expand Down Expand Up @@ -257,6 +260,71 @@ def test_root_commands(local_chain, wallet_setup):
)
assert "✅ Finalized" in undelegate_alice.stdout

# TODO: Ask nucleus the rate limit and wait epoch
# Sleep 120 seconds for rate limiting when unstaking
print("Waiting for interval for 2 minutes")
time.sleep(120)

# Stake to delegate Bob from Alice
stake_delegate = exec_command_alice(
command="root",
sub_command="delegate-stake",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--chain",
"ws://127.0.0.1:9945",
"--wallet-name",
wallet_alice.name,
"--delegate-ss58key",
wallet_bob.hotkey.ss58_address,
"--network",
"local",
"--all",
"--no-prompt",
],
)
assert "✅ Finalized" in stake_delegate.stdout

# First row are headers, records start from second row
alice_delegates_info = alice_delegates.stdout.splitlines()[5].split()

# WALLET: Wallet name of Alice
assert alice_delegates_info[0] == wallet_alice.name

# SS58: address of the Bob's hotkey (Alice has staked to Bob)
assert wallet_bob.hotkey.ss58_address == alice_delegates_info[2]

# Delegation: This should be 999999 as Alice delegated 999999 TAO to Bob
delegate_stake = Balance.from_tao(string_tao_to_float(alice_delegates_info[3]))
assert delegate_stake == Balance.from_tao(999999)

# TODO: Ask nucleus the rate limit and wait epoch
# Sleep 120 seconds for rate limiting when unstaking
print("Waiting for interval for 2 minutes")
time.sleep(120)

# Unstake from Bob Delegate
undelegate_alice = exec_command_alice(
command="root",
sub_command="undelegate-stake",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--chain",
"ws://127.0.0.1:9945",
"--wallet-name",
wallet_alice.name,
"--delegate-ss58key",
wallet_bob.hotkey.ss58_address,
"--network",
"local",
"--all",
"--no-prompt",
],
)
assert "✅ Finalized" in undelegate_alice.stdout

print("✅ Passed Root commands")


Expand Down
Loading