Skip to content

Commit 48fb5ea

Browse files
committed
Fix stake add operation mapping for multi-target staking
1 parent f35a42b commit 48fb5ea

2 files changed

Lines changed: 257 additions & 121 deletions

File tree

bittensor_cli/src/commands/stake/add.py

Lines changed: 117 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -339,119 +339,134 @@ async def stake_extrinsic(
339339
)
340340

341341
# Determine the amount we are staking.
342-
rows = []
343-
amounts_to_stake = []
344-
current_stake_balances = []
345-
prices_with_tolerance = []
346-
remaining_wallet_balance = current_wallet_balance
347-
max_slippage = 0.0
348-
342+
operation_targets = []
349343
for hotkey in hotkeys_to_stake_to:
350344
for netuid in netuids:
351345
# Check that the subnet exists.
352346
subnet_info = all_subnets.get(netuid)
353347
if not subnet_info:
354348
print_error(f"Subnet with netuid: {netuid} does not exist.")
355349
continue
356-
current_stake_balances.append(hotkey_stake_map[hotkey[1]][netuid])
357-
358-
# Get the amount.
359-
amount_to_stake = Balance(0)
360-
if amount:
361-
amount_to_stake = Balance.from_tao(amount)
362-
elif stake_all:
363-
amount_to_stake = current_wallet_balance / len(netuids)
364-
elif not amount:
365-
amount_to_stake, _ = _prompt_stake_amount(
366-
current_balance=remaining_wallet_balance,
367-
netuid=netuid,
368-
action_name="stake",
369-
)
370-
amounts_to_stake.append(amount_to_stake)
350+
operation_targets.append(
351+
(hotkey, netuid, subnet_info, hotkey_stake_map[hotkey[1]][netuid])
352+
)
371353

372-
# Check enough to stake.
373-
if amount_to_stake > remaining_wallet_balance:
374-
print_error(
375-
f"Not enough stake:[bold white]\n wallet balance:{remaining_wallet_balance} < "
376-
f"staking amount: {amount_to_stake}[/bold white]"
377-
)
378-
return
379-
remaining_wallet_balance -= amount_to_stake
380-
381-
# Calculate slippage
382-
# TODO: Update for V3, slippage calculation is significantly different in v3
383-
# try:
384-
# received_amount, slippage_pct, slippage_pct_float, rate = (
385-
# _calculate_slippage(subnet_info, amount_to_stake, stake_fee)
386-
# )
387-
# except ValueError:
388-
# return False
389-
#
390-
# max_slippage = max(slippage_pct_float, max_slippage)
391-
392-
# Temporary workaround - calculations without slippage
393-
current_price_float = float(subnet_info.price.tao)
394-
rate = _safe_inverse_rate(current_price_float)
395-
396-
# If we are staking safe, add price tolerance
397-
if safe_staking:
398-
if subnet_info.is_dynamic:
399-
price_with_tolerance = current_price_float * (1 + rate_tolerance)
400-
_rate_with_tolerance = _safe_inverse_rate(
401-
price_with_tolerance
402-
) # Rate only for display
403-
rate_with_tolerance = f"{_rate_with_tolerance:.4f}"
404-
price_with_tolerance = Balance.from_tao(
405-
price_with_tolerance
406-
) # Actual price to pass to extrinsic
407-
else:
408-
rate_with_tolerance = "1"
409-
price_with_tolerance = Balance.from_rao(1)
410-
extrinsic_fee = await get_stake_extrinsic_fee(
411-
netuid_=netuid,
412-
amount_=amount_to_stake,
413-
staking_address_=hotkey[1],
414-
safe_staking_=safe_staking,
415-
price_limit=price_with_tolerance,
416-
)
417-
prices_with_tolerance.append(price_with_tolerance)
418-
row_extension = [
419-
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
420-
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
421-
# safe staking
422-
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
423-
]
354+
if stake_all and not operation_targets:
355+
print_error("No valid staking operations to perform.")
356+
return
357+
358+
rows = []
359+
operations = []
360+
remaining_wallet_balance = current_wallet_balance
361+
max_slippage = 0.0
362+
363+
for hotkey, netuid, subnet_info, current_stake_balance in operation_targets:
364+
staking_address = hotkey[1]
365+
366+
# Get the amount.
367+
amount_to_stake = Balance(0)
368+
if amount:
369+
amount_to_stake = Balance.from_tao(amount)
370+
elif stake_all:
371+
amount_to_stake = current_wallet_balance / len(operation_targets)
372+
elif not amount:
373+
amount_to_stake, _ = _prompt_stake_amount(
374+
current_balance=remaining_wallet_balance,
375+
netuid=netuid,
376+
action_name="stake",
377+
)
378+
379+
# Check enough to stake.
380+
if amount_to_stake > remaining_wallet_balance:
381+
print_error(
382+
f"Not enough stake:[bold white]\n wallet balance:{remaining_wallet_balance} < "
383+
f"staking amount: {amount_to_stake}[/bold white]"
384+
)
385+
return
386+
remaining_wallet_balance -= amount_to_stake
387+
388+
# Calculate slippage
389+
# TODO: Update for V3, slippage calculation is significantly different in v3
390+
# try:
391+
# received_amount, slippage_pct, slippage_pct_float, rate = (
392+
# _calculate_slippage(subnet_info, amount_to_stake, stake_fee)
393+
# )
394+
# except ValueError:
395+
# return False
396+
#
397+
# max_slippage = max(slippage_pct_float, max_slippage)
398+
399+
# Temporary workaround - calculations without slippage
400+
current_price_float = float(subnet_info.price.tao)
401+
rate = _safe_inverse_rate(current_price_float)
402+
price_with_tolerance = None
403+
404+
# If we are staking safe, add price tolerance
405+
if safe_staking:
406+
if subnet_info.is_dynamic:
407+
price_with_tolerance = current_price_float * (1 + rate_tolerance)
408+
_rate_with_tolerance = _safe_inverse_rate(
409+
price_with_tolerance
410+
) # Rate only for display
411+
rate_with_tolerance = f"{_rate_with_tolerance:.4f}"
412+
price_with_tolerance = Balance.from_tao(
413+
price_with_tolerance
414+
) # Actual price to pass to extrinsic
424415
else:
425-
extrinsic_fee = await get_stake_extrinsic_fee(
426-
netuid_=netuid,
427-
amount_=amount_to_stake,
428-
staking_address_=hotkey[1],
429-
safe_staking_=safe_staking,
430-
)
431-
row_extension = []
432-
# TODO this should be asyncio gathered before the for loop
433-
amount_minus_fee = (
434-
(amount_to_stake - extrinsic_fee) if not proxy else amount_to_stake
416+
rate_with_tolerance = "1"
417+
price_with_tolerance = Balance.from_rao(1)
418+
extrinsic_fee = await get_stake_extrinsic_fee(
419+
netuid_=netuid,
420+
amount_=amount_to_stake,
421+
staking_address_=staking_address,
422+
safe_staking_=safe_staking,
423+
price_limit=price_with_tolerance,
424+
)
425+
row_extension = [
426+
f"{rate_with_tolerance} {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ",
427+
f"[{'dark_sea_green3' if allow_partial_stake else 'red'}]"
428+
# safe staking
429+
f"{allow_partial_stake}[/{'dark_sea_green3' if allow_partial_stake else 'red'}]",
430+
]
431+
else:
432+
extrinsic_fee = await get_stake_extrinsic_fee(
433+
netuid_=netuid,
434+
amount_=amount_to_stake,
435+
staking_address_=staking_address,
436+
safe_staking_=safe_staking,
435437
)
436-
sim_swap = await subtensor.sim_swap(
437-
origin_netuid=0,
438-
destination_netuid=netuid,
439-
amount=amount_minus_fee.rao,
438+
row_extension = []
439+
# TODO this should be asyncio gathered before the for loop
440+
amount_minus_fee = (
441+
(amount_to_stake - extrinsic_fee) if not proxy else amount_to_stake
442+
)
443+
sim_swap = await subtensor.sim_swap(
444+
origin_netuid=0,
445+
destination_netuid=netuid,
446+
amount=amount_minus_fee.rao,
447+
)
448+
received_amount = sim_swap.alpha_amount
449+
# Add rows for the table
450+
base_row = [
451+
str(netuid), # netuid
452+
f"{staking_address}", # hotkey
453+
str(amount_to_stake), # amount
454+
str(rate) + f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
455+
str(received_amount.set_unit(netuid)), # received
456+
str(sim_swap.tao_fee), # fee
457+
str(extrinsic_fee),
458+
# str(slippage_pct), # slippage
459+
] + row_extension
460+
rows.append(tuple(base_row))
461+
operations.append(
462+
(
463+
netuid,
464+
staking_address,
465+
amount_to_stake,
466+
current_stake_balance,
467+
price_with_tolerance,
440468
)
441-
received_amount = sim_swap.alpha_amount
442-
# Add rows for the table
443-
base_row = [
444-
str(netuid), # netuid
445-
f"{hotkey[1]}", # hotkey
446-
str(amount_to_stake), # amount
447-
str(rate)
448-
+ f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate
449-
str(received_amount.set_unit(netuid)), # received
450-
str(sim_swap.tao_fee), # fee
451-
str(extrinsic_fee),
452-
# str(slippage_pct), # slippage
453-
] + row_extension
454-
rows.append(tuple(base_row))
469+
)
455470

456471
# Define and print stake table + slippage warning
457472
table = _define_stake_table(wallet, subtensor, safe_staking, rate_tolerance)
@@ -467,23 +482,6 @@ async def stake_extrinsic(
467482
if not unlock_key(wallet).success:
468483
return
469484

470-
# Build the list of (netuid, hotkey, amount, current_stake, price_limit) tuples
471-
# that describe each staking operation we need to perform.
472-
# The zip aligns netuids with amounts/balances (which are populated per
473-
# hotkey-netuid pair, but the zip truncates to len(netuids), matching the
474-
# original execution order). Each netuid's amount/price applies to all hotkeys.
475-
operations = []
476-
if safe_staking:
477-
for ni, am, curr, price in zip(
478-
netuids, amounts_to_stake, current_stake_balances, prices_with_tolerance
479-
):
480-
for _, staking_address in hotkeys_to_stake_to:
481-
operations.append((ni, staking_address, am, curr, price))
482-
else:
483-
for ni, am, curr in zip(netuids, amounts_to_stake, current_stake_balances):
484-
for _, staking_address in hotkeys_to_stake_to:
485-
operations.append((ni, staking_address, am, curr, None))
486-
487485
total_ops = len(operations)
488486
use_batch = total_ops > 1
489487

0 commit comments

Comments
 (0)