Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
069883e
Pure proxy create init
thewhaleking Nov 6, 2025
ee4fd78
Actually run the command
thewhaleking Nov 6, 2025
8cf030d
More proxies
thewhaleking Nov 6, 2025
17b731d
Import
thewhaleking Nov 6, 2025
db73674
Kill proxy pure
thewhaleking Nov 6, 2025
57624fe
Add proxy support to sign_and_send_extrinsic
thewhaleking Nov 6, 2025
458fb70
Subnets Register proxy added
thewhaleking Nov 6, 2025
4effee2
Docstring
thewhaleking Nov 6, 2025
8b5a79e
Transfer proxy added
thewhaleking Nov 6, 2025
14163dd
Swap hotkey
thewhaleking Nov 6, 2025
c8e7329
Add spawner to proxy kill
thewhaleking Nov 6, 2025
1ff4795
Better proxy stuff
thewhaleking Nov 6, 2025
b29728d
proxy help text
chideraao Nov 6, 2025
14b755d
Working kill
thewhaleking Nov 6, 2025
fe6dee1
More added
thewhaleking Nov 7, 2025
ac78ee1
Stake Remove
thewhaleking Nov 7, 2025
9e6078b
Add debug loggers for proxy app
thewhaleking Nov 7, 2025
d774433
Move stake
thewhaleking Nov 7, 2025
c181fb9
Transfer stake
thewhaleking Nov 7, 2025
6384be2
Add docstring
thewhaleking Nov 7, 2025
83408c1
Made verbosity_handler also handle prompt/json-output
thewhaleking Nov 7, 2025
da1e431
Allow for adding created pure proxy to address book.
thewhaleking Nov 7, 2025
c89ea3d
Updated kill confirmation
thewhaleking Nov 7, 2025
4be011c
Updated proxies to new file
thewhaleking Nov 7, 2025
d194e47
Cleanup
thewhaleking Nov 7, 2025
642f3d3
Proxy address book new command: remove
thewhaleking Nov 7, 2025
78457eb
Help panels
thewhaleking Nov 7, 2025
2634703
Stake swap
thewhaleking Nov 7, 2025
9800121
Child keys
thewhaleking Nov 7, 2025
cdae3da
proxy help text
chideraao Nov 7, 2025
b98f29d
updated proxy types
chideraao Nov 7, 2025
7177d49
Merge branch 'opentensor:feat/thewhaleking/proxy-app' into feat/thewh…
chideraao Nov 7, 2025
7d7ba3b
Subnets and sudo
thewhaleking Nov 7, 2025
7829ad1
Weights
thewhaleking Nov 7, 2025
954bb9a
Stake
thewhaleking Nov 7, 2025
5f09078
Fixed Annotated
thewhaleking Nov 7, 2025
4344547
Merge pull request #707 from chideraao/feat/thewhaleking/proxy-app
thewhaleking Nov 10, 2025
912949c
Ruff
thewhaleking Nov 10, 2025
451f963
Merge branch 'staging' into feat/thewhaleking/proxy-app
thewhaleking Nov 10, 2025
c066e03
Crowdloan
thewhaleking Nov 11, 2025
17e1342
Get extrinsic fee
thewhaleking Nov 11, 2025
9f5b967
Balance calculations.
thewhaleking Nov 11, 2025
4c7c2d9
More balances
thewhaleking Nov 11, 2025
c853d89
fmt
thewhaleking Nov 11, 2025
ff4f64a
Handle `w transfer` warning for when the reported owner is the genesi…
thewhaleking Nov 11, 2025
9d20db9
Genesis
thewhaleking Nov 11, 2025
bda7b3d
Better handle real account pays extrinsics fee for proxy transactions.
thewhaleking Nov 11, 2025
c940963
Ruff
thewhaleking Nov 11, 2025
dd91d0e
Wording
thewhaleking Nov 11, 2025
e675ee2
Handle pre-3.11 StrEnum
thewhaleking Nov 12, 2025
3dbdcaf
Added TODOs
thewhaleking Nov 12, 2025
3cb100b
Add TODO
thewhaleking Nov 12, 2025
54054a1
Add TODO
thewhaleking Nov 12, 2025
a12da83
Handle non-sn owner sudo set
thewhaleking Nov 14, 2025
08165a3
Added `--announce-only` param
thewhaleking Nov 14, 2025
5c7c535
Better formatting, param handling
thewhaleking Nov 14, 2025
162d3d4
Address book in sqlite
thewhaleking Nov 14, 2025
05f348b
Config mgmt of new sqlite proxy address book
thewhaleking Nov 14, 2025
05c953b
Add config update-proxy cmd
thewhaleking Nov 14, 2025
9bb3ebd
Updated with context manager
thewhaleking Nov 14, 2025
c540493
Updated defaults
thewhaleking Nov 14, 2025
889dc70
Merge branch 'staging' into feat/thewhaleking/proxy-app
thewhaleking Nov 19, 2025
49a0fb8
Name shadowing
thewhaleking Nov 19, 2025
4b96999
Announcements beginning
thewhaleking Nov 19, 2025
6f01824
Improved proxy address book
thewhaleking Nov 19, 2025
658c689
Improved proxy address book
thewhaleking Nov 19, 2025
77a309e
Updated text
thewhaleking Nov 19, 2025
87cb711
Updated text
thewhaleking Nov 19, 2025
744bdf6
Added delay confirmation to create/add
thewhaleking Nov 19, 2025
0e0a1ed
More announcement work
thewhaleking Nov 19, 2025
70ec7ed
Typo
thewhaleking Nov 19, 2025
6bd022c
Proxy announce executed
thewhaleking Nov 19, 2025
7eca41f
Proxy announce execute improved
thewhaleking Nov 19, 2025
f12f591
Add ability to specify call hex
thewhaleking Nov 19, 2025
8b5c457
Allow for 0x
thewhaleking Nov 19, 2025
4491fe0
Added TODO
thewhaleking Nov 25, 2025
3e9a1da
Merge branch 'staging' into feat/thewhaleking/proxy-app
thewhaleking Nov 25, 2025
273742a
Added optionally specified `real` param to proxy execute announced.
thewhaleking Nov 25, 2025
4b1d8a4
Updated balance check in w transfer to account for proxy.
thewhaleking Nov 25, 2025
e0251e2
Added some unit tests
thewhaleking Nov 25, 2025
f92750e
WIP adding tests
thewhaleking Nov 26, 2025
236bd36
WIP
thewhaleking Nov 27, 2025
e3b6791
Passing
thewhaleking Nov 27, 2025
ef12257
Nicely working test for pure proxies.
thewhaleking Nov 28, 2025
7aceee6
Removed unused imports
thewhaleking Nov 28, 2025
f9c5b77
Proxy add/remove test
thewhaleking Nov 28, 2025
9881a35
Fixed tests
thewhaleking Dec 1, 2025
26473d2
Update type annotation
thewhaleking Dec 2, 2025
230516a
Add docstrings, update return type.
thewhaleking Dec 2, 2025
d4196be
Bumped asi version
thewhaleking Dec 3, 2025
6ac50bb
Bumped asi version
thewhaleking Dec 3, 2025
c301505
Merge branch 'staging' into feat/thewhaleking/proxy-app
thewhaleking Dec 5, 2025
3e8c1eb
Merge
thewhaleking Dec 5, 2025
0a6968d
Check for success before getting extrinsic identifier
thewhaleking Dec 5, 2025
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
1,775 changes: 1,393 additions & 382 deletions bittensor_cli/cli.py

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions bittensor_cli/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ class config:
},
}

class proxies:
base_path = "~/.bittensor"
path = "~/.bittensor/bittensor.db"
dictionary = {}

class subtensor:
network = "finney"
chain_endpoint = None
Expand Down Expand Up @@ -734,6 +739,9 @@ class RootSudoOnly(Enum):
"PARTICIPANT": "Crowdloan Participation",
"INFO": "Crowdloan Information",
},
"PROXY": {
"MGMT": "Proxy Account Management",
},
}


Expand Down
13 changes: 10 additions & 3 deletions bittensor_cli/src/bittensor/extrinsics/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ async def burned_register_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
era: Optional[int] = None,
proxy: Optional[str] = None,
) -> tuple[bool, str, Optional[str]]:
"""Registers the wallet to chain by recycling TAO.

Expand All @@ -693,7 +694,7 @@ async def burned_register_extrinsic(
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
or returns `False` if the extrinsic fails to be finalized within the timeout.
:param era: the period (in blocks) for which the transaction should remain valid.
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
:param proxy: the proxy address to use for the call.

:return: (success, msg), where success is `True` if extrinsic was finalized or included in the block. If we did not
wait for finalization/inclusion, the response is `True`.
Expand Down Expand Up @@ -758,7 +759,12 @@ async def burned_register_extrinsic(
},
)
success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet, wait_for_inclusion, wait_for_finalization, era=era_
call,
wallet,
wait_for_inclusion,
wait_for_finalization,
era=era_,
proxy=proxy,
)

if not success:
Expand Down Expand Up @@ -1752,6 +1758,7 @@ async def swap_hotkey_extrinsic(
wallet: Wallet,
new_wallet: Wallet,
netuid: Optional[int] = None,
proxy: Optional[str] = None,
prompt: bool = False,
) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""
Expand Down Expand Up @@ -1837,7 +1844,7 @@ async def swap_hotkey_extrinsic(
call_params=call_params,
)
success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
call, wallet
call=call, wallet=wallet, proxy=proxy
)

if success:
Expand Down
4 changes: 3 additions & 1 deletion bittensor_cli/src/bittensor/extrinsics/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ async def root_register_extrinsic(
wallet: Wallet,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = True,
proxy: Optional[str] = None,
) -> tuple[bool, str, Optional[str]]:
r"""Registers the wallet to root network.

Expand All @@ -301,7 +302,7 @@ async def root_register_extrinsic(
`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`,
or returns `False` if the extrinsic fails to be finalized within the timeout.
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
:param proxy: Optional proxy to use for making the call.

:return: (success, msg), with success being `True` if extrinsic was finalized or included in the block. If we did
not wait for finalization/inclusion, the response is `True`.
Expand Down Expand Up @@ -331,6 +332,7 @@ async def root_register_extrinsic(
wallet=wallet,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
proxy=proxy,
)

if not success:
Expand Down
126 changes: 69 additions & 57 deletions bittensor_cli/src/bittensor/extrinsics/transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ async def transfer_extrinsic(
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = False,
proxy: Optional[str] = None,
announce_only: bool = False,
) -> tuple[bool, Optional[AsyncExtrinsicReceipt]]:
"""Transfers funds from this wallet to the destination public key address.

Expand All @@ -48,6 +50,9 @@ async def transfer_extrinsic(
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning
`True`, or returns `False` if the extrinsic fails to be finalized within the timeout.
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
:param proxy: Optional proxy to use for this call.
:param announce_only: If set along with proxy, will make this call as an announcement, rather than making the call

:return: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for
finalization / inclusion, the response is `True`, regardless of its inclusion.
"""
Expand All @@ -63,22 +68,11 @@ async def get_transfer_fee() -> Balance:
call_function=call_function,
call_params=call_params,
)
return await subtensor.get_extrinsic_fee(
call=call, keypair=wallet.coldkeypub, proxy=proxy
)

try:
payment_info = await subtensor.substrate.get_payment_info(
call=call, keypair=wallet.coldkeypub
)
except SubstrateRequestException as e:
payment_info = {"partial_fee": int(2e7)} # assume 0.02 Tao
err_console.print(
f":cross_mark: [red]Failed to get payment info[/red]:[bold white]\n"
f" {format_error_message(e)}[/bold white]\n"
f" Defaulting to default transfer fee: {payment_info['partialFee']}"
)

return Balance.from_rao(payment_info["partial_fee"])

async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
async def do_transfer() -> tuple[bool, str, str, Optional[AsyncExtrinsicReceipt]]:
"""
Makes transfer from wallet to destination public key address.
:return: success, block hash, formatted error message
Expand All @@ -88,29 +82,17 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
call_function=call_function,
call_params=call_params,
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call, keypair=wallet.coldkey, era={"period": era}
)
response = await subtensor.substrate.submit_extrinsic(
extrinsic,
wait_for_inclusion=wait_for_inclusion,
success_, error_msg_, receipt_ = await subtensor.sign_and_send_extrinsic(
call=call,
wallet=wallet,
wait_for_finalization=wait_for_finalization,
wait_for_inclusion=wait_for_inclusion,
proxy=proxy,
era={"period": era},
announce_only=announce_only,
)
# We only wait here if we expect finalization.
if not wait_for_finalization and not wait_for_inclusion:
return True, "", "", response

# Otherwise continue with finalization.
if await response.is_success:
block_hash_ = response.block_hash
return True, block_hash_, "", response
else:
return (
False,
"",
format_error_message(await response.error_message),
response,
)
block_hash_ = receipt_.block_hash if receipt_ is not None else ""
return success_, block_hash_, error_msg_, receipt_

# Validate destination address.
if not is_valid_bittensor_address_or_public_key(destination):
Expand Down Expand Up @@ -142,36 +124,66 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
# check existential deposit and fee
print_verbose("Fetching existential and fee", status)
block_hash = await subtensor.substrate.get_chain_head()
account_balance, existential_deposit = await asyncio.gather(
if proxy:
proxy_balance = await subtensor.get_balance(proxy, block_hash=block_hash)
account_balance, existential_deposit, fee = await asyncio.gather(
subtensor.get_balance(
wallet.coldkeypub.ss58_address, block_hash=block_hash
),
subtensor.get_existential_deposit(block_hash=block_hash),
get_transfer_fee(),
)
fee = await get_transfer_fee()

if allow_death:
# Check if the transfer should keep alive the account
existential_deposit = Balance(0)

if account_balance < (amount + fee + existential_deposit) and not allow_death:
err_console.print(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
f" for fee: [bright_cyan]{fee}[/bright_cyan]\n"
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
f"You can try again with `--allow-death`."
)
return False, None
elif account_balance < (amount + fee) and allow_death:
print_error(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_red]{account_balance}[/bright_red]\n"
f" amount: [bright_red]{amount}[/bright_red]\n"
f" for fee: [bright_red]{fee}[/bright_red]"
)
return False, None
if proxy:
if proxy_balance < (amount + existential_deposit) and not allow_death:
err_console.print(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_cyan]{proxy_balance}[/bright_cyan]\n"
f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
f"You can try again with `--allow-death`."
)
return False, None
if account_balance < fee:
err_console.print(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
f" fee: [bright_cyan]{fee}[/bright_cyan]\n"
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
)
return False, None
if account_balance < amount and allow_death:
print_error(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_red]{account_balance}[/bright_red]\n"
f" amount: [bright_red]{amount}[/bright_red]\n"
)
return False, None
else:
if account_balance < (amount + fee + existential_deposit) and not allow_death:
err_console.print(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_cyan]{account_balance}[/bright_cyan]\n"
f" amount: [bright_cyan]{amount}[/bright_cyan]\n"
f" for fee: [bright_cyan]{fee}[/bright_cyan]\n"
f" would bring you under the existential deposit: [bright_cyan]{existential_deposit}[/bright_cyan].\n"
f"You can try again with `--allow-death`."
)
return False, None
elif account_balance < (amount + fee) and allow_death:
print_error(
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
f" balance: [bright_red]{account_balance}[/bright_red]\n"
f" amount: [bright_red]{amount}[/bright_red]\n"
f" for fee: [bright_red]{fee}[/bright_red]"
)
return False, None
if proxy:
account_balance = proxy_balance

# Ask before moving on.
if prompt:
Expand Down Expand Up @@ -213,7 +225,7 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
if success:
with console.status(":satellite: Checking Balance...", spinner="aesthetic"):
new_balance = await subtensor.get_balance(
wallet.coldkeypub.ss58_address, reuse_block=False
proxy or wallet.coldkeypub.ss58_address, reuse_block=False
)
console.print(
f"Balance:\n"
Expand Down
Loading