Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
51 changes: 46 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,52 @@
# Changelog

## 9.0.0rc4 /2025-02-12
* Adds sort option to metagraph/show
* Updates metagraph/show to use direct dividends instead of relative
* Updates subnets list to use working tao emissions
## 9.0.0 /2025-02-13

## 9.0.0rc3 /2025-02-11
## What's Changed
* Btcli ported to Rao by @ibraheem-opentensor & @thewhaleking in https://github.com/opentensor/btcli/tree/rao-games/pools
* fix netuid from str to int by @roman-opentensor in https://github.com/opentensor/btcli/pull/195
* add runtime apis to reg by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/196
* Updated tables (st list, s list) by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/200
* Modifying descriptions and links in stake and subnets dot py files by @rajkaramchedu in https://github.com/opentensor/btcli/pull/246
* Fixes Identity Lookup (Rao Games Pools) by @thewhaleking in https://github.com/opentensor/btcli/pull/279
* Show encrypted hotkeys in w list by @thewhaleking in https://github.com/opentensor/btcli/pull/288
* Backmerge rao branch to decoding branch by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/290
* Updates identity, sn identity, and other chain stuff by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/292
* Updates Rao to decode using chain by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/286
* Fix/rao remove mention of cost by @camfairchild in https://github.com/opentensor/btcli/pull/293
* Uses uvloop if it's installed by @thewhaleking in https://github.com/opentensor/btcli/pull/294
* Feat: Safe staking by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/299
* Removes stake from w balances by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/301
* Updates docstrings for commands by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/303
* Release/9.0.0rc4 by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/306
* Rao to staging merge (new branch) by @thewhaleking in https://github.com/opentensor/btcli/pull/305
* [WIP] Rao by @thewhaleking in https://github.com/opentensor/btcli/pull/129
* Updates e2e tests for rao by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/307
* Update dividends, adds sort by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/308
* Final cleanups for Rao by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/309

## New Contributors
* @camfairchild made their first contribution in https://github.com/opentensor/btcli/pull/293

**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.4.4...v9.0.0

## 8.4.4 /2025-02-07 - 18:30 PST

## What's Changed
* Proposals info fix (for dtao governance vote) by @ibraheem-opentensor

**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.4.3...v8.4.4

## 8.4.3 /2025-01-23

## What's Changed
* Backmerge main to staging 842 by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/273
* Fix arg order for set-identity by @thewhaleking in https://github.com/opentensor/btcli/pull/282
* Adds updates to btwallet3, adds overwrite flag and updates tests by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/275

**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.4.2...v8.4.3

## 8.4.2 /2024-12-12

## What's Changed
* Removes the `.value` checks as we no longer use SCALE objects. by @thewhaleking in https://github.com/opentensor/btcli/pull/270
Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
from .cli import CLIManager


__version__ = "9.0.0rc4"
__version__ = "9.0.0"

__all__ = ["CLIManager", "__version__"]
6 changes: 4 additions & 2 deletions bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class GitError(Exception):
pass


__version__ = "9.0.0rc4"
__version__ = "9.0.0"


_core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
Expand Down Expand Up @@ -4072,7 +4072,9 @@ def sudo_proposals(
[green]$[/green] btcli sudo proposals
"""
self.verbosity_handler(quiet, verbose)
return self._run_command(sudo.proposals(self.initialize_chain(network)))
return self._run_command(
sudo.proposals(self.initialize_chain(network), verbose)
)

def sudo_senate_vote(
self,
Expand Down
29 changes: 29 additions & 0 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1355,3 +1355,32 @@ def hex_to_bytes(hex_str: str) -> bytes:
else:
bytes_result = bytes.fromhex(hex_str)
return bytes_result


def blocks_to_duration(blocks: int) -> str:
"""Convert blocks to human readable duration string using two largest units.

Args:
blocks (int): Number of blocks (12s per block)

Returns:
str: Duration string like '2d 5h', '3h 45m', '2m 10s', or '0s'
"""
if blocks <= 0:
return "0s"

seconds = blocks * 12
intervals = [
("d", 86400), # 60 * 60 * 24
("h", 3600), # 60 * 60
("m", 60),
("s", 1),
]
results = []
for unit, seconds_per_unit in intervals:
unit_count = seconds // seconds_per_unit
seconds %= seconds_per_unit
if unit_count > 0:
results.append(f"{unit_count}{unit}")
# Return only the first two non-zero units
return " ".join(results[:2]) or "0s"
4 changes: 2 additions & 2 deletions bittensor_cli/src/commands/stake/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async def safe_stake_extrinsic(
return
else:
err_out(
f"\n{failure_prelude} with error: {format_error_message(e, subtensor.substrate)}"
f"\n{failure_prelude} with error: {format_error_message(e)}"
)
return
else:
Expand Down Expand Up @@ -181,7 +181,7 @@ async def stake_extrinsic(
)
except SubstrateRequestException as e:
err_out(
f"\n{failure_prelude} with error: {format_error_message(e, subtensor.substrate)}"
f"\n{failure_prelude} with error: {format_error_message(e)}"
)
return
else:
Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/src/commands/stake/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ async def _safe_unstake_extrinsic(
return
else:
err_out(
f"\n{failure_prelude} with error: {format_error_message(e, subtensor.substrate)}"
f"\n{failure_prelude} with error: {format_error_message(e)}"
)
return

Expand Down
69 changes: 52 additions & 17 deletions bittensor_cli/src/commands/sudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
print_verbose,
normalize_hyperparameters,
unlock_key,
blocks_to_duration,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -269,14 +270,22 @@ def format_call_data(call_data: dict) -> str:
call_info = call_details[0]
call_function, call_args = next(iter(call_info.items()))

# Extract the argument, handling tuple values
formatted_args = ", ".join(
str(arg[0]) if isinstance(arg, tuple) else str(arg)
for arg in call_args.values()
)
# Format arguments, handle nested/large payloads
formatted_args = []
for arg_name, arg_value in call_args.items():
if isinstance(arg_value, (tuple, list, dict)):
# For large nested, show abbreviated version
content_str = str(arg_value)
if len(content_str) > 20:
formatted_args.append(f"{arg_name}: ... [{len(content_str)}] ...")
else:
formatted_args.append(f"{arg_name}: {arg_value}")
else:
formatted_args.append(f"{arg_name}: {arg_value}")

# Format the final output string
return f"{call_function}({formatted_args})"
args_str = ", ".join(formatted_args)
return f"{module}.{call_function}({args_str})"


def _validate_proposal_hash(proposal_hash: str) -> bool:
Expand Down Expand Up @@ -576,24 +585,30 @@ async def get_senate(subtensor: "SubtensorInterface"):
return console.print(table)


async def proposals(subtensor: "SubtensorInterface"):
async def proposals(subtensor: "SubtensorInterface", verbose: bool):
console.print(
":satellite: Syncing with chain: [white]{}[/white] ...".format(
subtensor.network
)
)
print_verbose("Fetching senate members & proposals")
block_hash = await subtensor.substrate.get_chain_head()
senate_members, all_proposals = await asyncio.gather(
senate_members, all_proposals, current_block = await asyncio.gather(
_get_senate_members(subtensor, block_hash),
_get_proposals(subtensor, block_hash),
subtensor.substrate.get_block_number(block_hash),
)

print_verbose("Fetching member information from Chain")
registered_delegate_info: dict[
str, DelegatesDetails
] = await subtensor.get_delegate_identities()

title = (
f"[bold #4196D6]Bittensor Governance Proposals[/bold #4196D6]\n"
f"[steel_blue3]Current Block:[/steel_blue3] {current_block}\t"
f"[steel_blue3]Network:[/steel_blue3] {subtensor.network}\n\n"
f"[steel_blue3]Active Proposals:[/steel_blue3] {len(all_proposals)}\t"
f"[steel_blue3]Senate Size:[/steel_blue3] {len(senate_members)}\n"
)
table = Table(
Column(
"[white]HASH",
Expand All @@ -608,25 +623,45 @@ async def proposals(subtensor: "SubtensorInterface"):
style="rgb(50,163,219)",
),
Column("[white]END", style="bright_cyan"),
Column("[white]CALLDATA", style="dark_sea_green"),
title=f"\n[dark_orange]Proposals\t\t\nActive Proposals: {len(all_proposals)}\t\tSenate Size: {len(senate_members)}\nNetwork: {subtensor.network}",
Column("[white]CALLDATA", style="dark_sea_green", width=30),
title=title,
show_footer=True,
box=box.SIMPLE_HEAVY,
pad_edge=False,
width=None,
border_style="bright_black",
)
for hash_, (call_data, vote_data) in all_proposals.items():
blocks_remaining = vote_data.end - current_block
if blocks_remaining > 0:
duration_str = blocks_to_duration(blocks_remaining)
vote_end_cell = f"{vote_data.end} [dim](in {duration_str})[/dim]"
else:
vote_end_cell = f"{vote_data.end} [red](expired)[/red]"

ayes_threshold = (
(len(vote_data.ayes) / vote_data.threshold * 100)
if vote_data.threshold > 0
else 0
)
nays_threshold = (
(len(vote_data.nays) / vote_data.threshold * 100)
if vote_data.threshold > 0
else 0
)
table.add_row(
hash_,
hash_ if verbose else f"{hash_[:4]}...{hash_[-4:]}",
str(vote_data.threshold),
str(len(vote_data.ayes)),
str(len(vote_data.nays)),
f"{len(vote_data.ayes)} ({ayes_threshold:.2f}%)",
f"{len(vote_data.nays)} ({nays_threshold:.2f}%)",
display_votes(vote_data, registered_delegate_info),
str(vote_data.end),
vote_end_cell,
format_call_data(call_data),
)
return console.print(table)
console.print(table)
console.print(
"\n[dim]* Both Ayes and Nays percentages are calculated relative to the proposal's threshold.[/dim]"
)


async def senate_vote(
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
wheel
async-property==0.2.2
async-substrate-interface>=1.0.0rc14
async-substrate-interface>=1.0.0
aiohttp~=3.10.2
backoff~=2.2.1
GitPython>=3.0.0
Expand All @@ -16,7 +16,7 @@ rich~=13.7
scalecodec==1.2.11
typer~=0.12
websockets>=14.1
bittensor-wallet>=3.0.2
bittensor-wallet>=3.0.3
plotille
pywry
plotly
21 changes: 12 additions & 9 deletions tests/e2e_tests/test_senate.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ def test_senate(local_chain, wallet_setup):
extra_args=[
"--chain",
"ws://127.0.0.1:9945",
"--verbose",
],
)
proposals_output = proposals.stdout.splitlines()[8].split()
proposals_output = proposals.stdout.splitlines()[9].split()

# Assert the hash is of correct format
assert len(proposals_output[0]) == 66
Expand All @@ -112,7 +113,7 @@ def test_senate(local_chain, wallet_setup):
assert proposals_output[2] == "0"

# 0 Nayes for the proposal
assert proposals_output[3] == "0"
assert proposals_output[4] == "0"

# Assert initial threshold is 3
assert proposals_output[1] == "3"
Expand Down Expand Up @@ -145,19 +146,20 @@ def test_senate(local_chain, wallet_setup):
extra_args=[
"--chain",
"ws://127.0.0.1:9945",
"--verbose",
],
)
proposals_after_aye_output = proposals_after_aye.stdout.splitlines()[8].split()
proposals_after_aye_output = proposals_after_aye.stdout.splitlines()[9].split()

# Assert Bob's vote is shown as aye
assert proposals_after_aye_output[4].strip(":") == wallet_bob.hotkey.ss58_address
assert proposals_after_aye_output[5] == "Aye"
assert proposals_after_aye_output[6].strip(":") == wallet_bob.hotkey.ss58_address
assert proposals_after_aye_output[7] == "Aye"

# Aye votes increased to 1
assert proposals_after_aye_output[2] == "1"

# Nay votes remain 0
assert proposals_after_aye_output[3] == "0"
assert proposals_after_aye_output[4] == "0"

# Register Alice to the root network (0)
# Registering to root automatically makes you a senator if eligible
Expand Down Expand Up @@ -208,18 +210,19 @@ def test_senate(local_chain, wallet_setup):
extra_args=[
"--chain",
"ws://127.0.0.1:9945",
"--verbose",
],
)
proposals_after_nay_output = proposals_after_nay.stdout.splitlines()

# Total Ayes to remain 1
proposals_after_nay_output[8].split()[2] == "1"
proposals_after_nay_output[9].split()[2] == "1"

# Total Nays increased to 1
proposals_after_nay_output[8].split()[3] == "1"
proposals_after_nay_output[9].split()[4] == "1"

# Assert Alice has voted Nay
proposals_after_nay_output[9].split()[0].strip(
proposals_after_nay_output[10].split()[0].strip(
":"
) == wallet_alice.hotkey.ss58_address

Expand Down
Loading