Edit on GitHub

communex.cli.key

  1import json
  2import re
  3from enum import Enum
  4from typing import Any, Optional, cast
  5
  6import typer
  7from substrateinterface import Keypair  # type: ignore
  8from typer import Context
  9
 10from communex._common import BalanceUnit, format_balance
 11from communex.cli._common import (get_universal_password, make_custom_context,
 12                                  print_table_from_plain_dict,
 13                                  print_table_standardize)
 14from communex.compat.key import (classic_key_path, classic_store_key,
 15                                 local_key_addresses, try_classic_load_key,
 16                                 try_load_key)
 17from communex.key import check_ss58_address, generate_keypair, is_ss58_address
 18from communex.misc import (local_keys_allbalance, local_keys_to_freebalance,
 19                           local_keys_to_stakedbalance)
 20
 21key_app = typer.Typer(no_args_is_help=True)
 22
 23
 24class SortBalance(str, Enum):
 25    all = "all"
 26    free = "free"
 27    staked = "staked"
 28
 29
 30@key_app.command()
 31def create(
 32    ctx: Context, name: str,
 33    password: str = typer.Option(None)
 34):
 35    """
 36    Generates a new key and stores it on a disk with the given name.
 37    """
 38    context = make_custom_context(ctx)
 39
 40    keypair = generate_keypair()
 41    address = keypair.ss58_address
 42
 43    context.info(f"Generated key with public address '{address}'.")
 44
 45    classic_store_key(keypair, name, password)
 46
 47    context.info(f"Key successfully stored with name '{name}'.")
 48
 49
 50@key_app.command()
 51def regen(
 52        ctx: Context, name: str, key_input: str, password: Optional[str] = None):
 53    """
 54    Stores the given key on a disk. Works with private key or mnemonic.
 55    """
 56    # TODO: secret input from env var and stdin
 57    context = make_custom_context(ctx)
 58    # Determine the input type based on the presence of spaces.
 59    if re.search(r'\s', key_input):
 60        # If mnemonic (contains spaces between words).
 61        keypair = Keypair.create_from_mnemonic(key_input)
 62        key_type = "mnemonic"
 63    else:
 64        # If private key (assumes no spaces).
 65        keypair = Keypair.create_from_private_key(key_input, ss58_format=42)
 66        key_type = "private key"
 67        # Substrate does not return these.
 68        keypair.mnemonic = ""  # type: ignore
 69        keypair.seed_hex = ""
 70
 71    address = keypair.ss58_address
 72    context.info(f"Loaded {key_type} with public address `{address}`.")
 73
 74    classic_store_key(keypair, name, password)
 75
 76    context.info(f"Key stored with name `{name}` successfully.")
 77
 78
 79@key_app.command()
 80def show(
 81    ctx: Context, key: str, show_private: bool = False,
 82    password: Optional[str] = None
 83):
 84    """
 85    Show information about a key.
 86    """
 87    context = make_custom_context(ctx)
 88
 89    path = classic_key_path(key)
 90    key_dict_json = try_load_key(path, context, password=password)
 91    key_dict = json.loads(key_dict_json)
 92
 93    if show_private is not True:
 94        key_dict["private_key"] = "[SENSITIVE-MODE]"
 95        key_dict["seed_hex"] = "[SENSITIVE-MODE]"
 96        key_dict["mnemonic"] = "[SENSITIVE-MODE]"
 97
 98    print_table_from_plain_dict(key_dict, ["Key", "Value"], context.console)
 99
100
101@key_app.command()
102def balances(
103    ctx: Context,
104    unit: BalanceUnit = BalanceUnit.joule,
105    sort_balance: SortBalance = SortBalance.all,
106    use_universal_password: bool = typer.Option(
107        False, help="""
108        If you want to use a password to decrypt all keys.
109        This will only work if all encrypted keys uses the same password.
110        If this is not the case, leave it blank and you will be prompted to give
111        every password.
112        """
113    )
114
115):
116    """
117    Gets balances of all keys.
118    """
119    context = make_custom_context(ctx)
120    client = context.com_client()
121    if use_universal_password:
122        universal_password = get_universal_password(context)
123    else:
124        universal_password = None
125
126    local_keys = local_key_addresses(context, universal_password=universal_password)
127    with context.console.status("Getting balances of all keys, this might take a while..."):
128        key2freebalance, key2stake = local_keys_allbalance(client, local_keys)
129    key_to_freebalance = {k: format_balance(
130        v, unit) for k, v in key2freebalance.items()}
131    key_to_stake = {k: format_balance(v, unit) for k, v in key2stake.items()}
132
133    key2balance = {k: v + key2stake[k] for k, v in key2freebalance.items()}
134    key_to_balance = {k: format_balance(v, unit)
135                      for k, v in key2balance.items()}
136
137    if sort_balance == SortBalance.all:
138        sorted_bal = {k: v for k, v in sorted(
139            key2balance.items(), key=lambda item: item[1], reverse=True)}
140    elif sort_balance == SortBalance.free:
141        sorted_bal = {k: v for k, v in sorted(
142            key2freebalance.items(), key=lambda item: item[1], reverse=True)}
143    elif sort_balance == SortBalance.staked:
144        sorted_bal = {k: v for k, v in sorted(
145            key2stake.items(), key=lambda item: item[1], reverse=True)}
146    else:
147        raise ValueError("Invalid sort balance option")
148
149    stake: list[str] = []
150    all_balance: list[str] = []
151    free: list[str] = []
152    keys: list[str] = []
153
154    for key, _ in sorted_bal.items():
155        keys.append(key)
156        free.append(key_to_freebalance[key])
157        stake.append(key_to_stake[key])
158        all_balance.append(key_to_balance[key])
159
160    pretty_dict = {
161        "key": keys,
162        "free": free,
163        "staked": stake,
164        "all": all_balance,
165    }
166
167    general_dict: dict[str, list[Any]] = cast(
168        dict[str, list[Any]], pretty_dict)
169    print_table_standardize(general_dict, context.console)
170
171
172@key_app.command(name='list')
173def inventory(
174    ctx: Context,
175    use_universal_password: bool = typer.Option(
176        False, help="""
177        Password to decrypt all keys.
178        This will only work if all encrypted keys uses the same password.
179        If this is not the case, leave it blank and you will be prompted to give
180        every password.
181        """
182    )
183):
184    """
185    Lists all keys stored on disk.
186    """
187    context = make_custom_context(ctx)
188    if use_universal_password:
189        universal_password = get_universal_password(context)
190    else:
191        universal_password = None
192    key_to_address = local_key_addresses(context, universal_password)
193    general_key_to_address: dict[str, str] = cast(
194        dict[str, str], key_to_address)
195    print_table_from_plain_dict(general_key_to_address, [
196                                "Key", "Address"], context.console)
197
198
199@key_app.command()
200def stakefrom(
201    ctx: Context, key: str,
202    unit: BalanceUnit = BalanceUnit.joule,
203    password: Optional[str] = None,
204):
205    """
206    Gets what keys is key staked from.
207    """
208    context = make_custom_context(ctx)
209    client = context.com_client()
210
211    if is_ss58_address(key):
212        key_address = key
213    else:
214        keypair = try_classic_load_key(key, context, password)
215        key_address = keypair.ss58_address
216        key_address = check_ss58_address(key_address)
217    with context.progress_status(f"Getting stake-from map for {key_address}..."):
218        result = client.get_stakefrom(key=key_address)
219
220    result = {k: format_balance(v, unit) for k, v in result.items()}
221
222    print_table_from_plain_dict(result, ["Key", "Stake"], context.console)
223
224
225@key_app.command()
226def staketo(
227    ctx: Context, key: str,
228    unit: BalanceUnit = BalanceUnit.joule,
229    password: Optional[str] = None,
230):
231    """
232    Gets stake to a key.
233    """
234    context = make_custom_context(ctx)
235    client = context.com_client()
236
237    if is_ss58_address(key):
238        key_address = key
239    else:
240        keypair = try_classic_load_key(key, context, password)
241        key_address = keypair.ss58_address
242        key_address = check_ss58_address(key_address)
243
244    with context.progress_status(f"Getting stake-to of {key_address}..."):
245        result = client.get_staketo(key=key_address)
246
247    result = {k: format_balance(v, unit) for k, v in result.items()}
248
249    print_table_from_plain_dict(result, ["Key", "Stake"], context.console)
250
251
252@key_app.command()
253def total_free_balance(
254    ctx: Context,
255    unit: BalanceUnit = BalanceUnit.joule,
256    use_universal_password: Optional[str] = typer.Option(
257        False, help="""
258        Password to decrypt all keys.
259        This will only work if all encrypted keys uses the same password.
260        If this is not the case, leave it blank and you will be prompted to give
261        every password.
262        """
263    )
264):
265    """
266    Returns total balance of all keys on a disk
267    """
268    context = make_custom_context(ctx)
269    client = context.com_client()
270
271    if use_universal_password:
272        universal_password = get_universal_password(context)
273    else:
274        universal_password = None
275    local_keys = local_key_addresses(context, universal_password)
276    with context.progress_status("Getting total free balance of all keys..."):
277        key2balance: dict[str, int] = local_keys_to_freebalance(client, local_keys)
278
279        balance_sum = sum(key2balance.values())
280
281        context.output(format_balance(balance_sum, unit=unit))
282
283
284@key_app.command()
285def total_staked_balance(
286    ctx: Context, unit: BalanceUnit = BalanceUnit.joule,
287    use_universal_password: bool = typer.Option(
288        False, help="""
289    Password to decrypt all keys.
290    This will only work if all encrypted keys uses the same password.
291    If this is not the case, leave it blank and you will be prompted to give
292    every password.
293    """
294    )
295):
296    """
297    Returns total stake of all keys on a disk
298    """
299    context = make_custom_context(ctx)
300    client = context.com_client()
301
302    if use_universal_password:
303        universal_password = get_universal_password(context)
304    else:
305        universal_password = None
306    local_keys = local_key_addresses(context, universal_password)
307    with context.progress_status("Getting total staked balance of all keys..."):
308        key2stake: dict[str, int] = local_keys_to_stakedbalance(
309            client, local_keys,
310        )
311
312        stake_sum = sum(key2stake.values())
313
314        context.output(format_balance(stake_sum, unit=unit))
315
316
317@key_app.command()
318def total_balance(
319    ctx: Context, unit: BalanceUnit = BalanceUnit.joule,
320    use_universal_password: bool = typer.Option(
321        False, help="""
322    Password to decrypt all keys.
323    This will only work if all encrypted keys uses the same password.
324    If this is not the case, leave it blank and you will be prompted to give
325    every password.
326    """
327    )
328):
329    """
330    Returns total tokens of all keys on a disk
331    """
332    context = make_custom_context(ctx)
333    client = context.com_client()
334
335    if use_universal_password:
336        universal_password = get_universal_password(context)
337    else:
338        universal_password = None
339    local_keys = local_key_addresses(context, universal_password)
340    with context.progress_status("Getting total tokens of all keys..."):
341        key2balance, key2stake = local_keys_allbalance(
342            client, local_keys
343        )
344        key2tokens = {k: v + key2stake[k] for k, v in key2balance.items()}
345        tokens_sum = sum(key2tokens.values())
346
347        context.output(format_balance(tokens_sum, unit=unit))
348
349
350@key_app.command()
351def power_delegation(
352    ctx: Context,
353    key: Optional[str] = None,
354    enable: bool = typer.Option(True, "--disable"),
355    use_universal_password: bool = typer.Option(False)
356):
357    """
358    Gets power delegation of a key.
359    """
360    context = make_custom_context(ctx)
361    client = context.com_client()
362    if key is None:
363        action = "enable" if enable else "disable"
364        confirm_message = (
365            f"Key was not set, this will {action} vote power delegation for all"
366            " keys on disk. Do you want to proceed?"
367        )
368        if not typer.confirm(confirm_message):
369            context.info("Aborted.")
370            exit(0)
371
372        if use_universal_password:
373            universal_password = get_universal_password(context)
374        else:
375            universal_password = None
376        local_keys = local_key_addresses(context, universal_password)
377    else:
378        local_keys = {key: None}
379    for key_name in local_keys.keys():
380        keypair = try_classic_load_key(key_name, context)
381        if enable is True:
382            context.info(f"Enabling vote power delegation on key {key_name} ...")
383            client.enable_vote_power_delegation(keypair)
384        else:
385            context.info(f"Disabling vote power delegation on key {key_name} ...")
386            client.disable_vote_power_delegation(keypair)
key_app = <typer.main.Typer object>
class SortBalance(builtins.str, enum.Enum):
25class SortBalance(str, Enum):
26    all = "all"
27    free = "free"
28    staked = "staked"

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

all = <SortBalance.all: 'all'>
free = <SortBalance.free: 'free'>
staked = <SortBalance.staked: 'staked'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@key_app.command()
def create( ctx: typer.models.Context, name: str, password: str = <typer.models.OptionInfo object>):
31@key_app.command()
32def create(
33    ctx: Context, name: str,
34    password: str = typer.Option(None)
35):
36    """
37    Generates a new key and stores it on a disk with the given name.
38    """
39    context = make_custom_context(ctx)
40
41    keypair = generate_keypair()
42    address = keypair.ss58_address
43
44    context.info(f"Generated key with public address '{address}'.")
45
46    classic_store_key(keypair, name, password)
47
48    context.info(f"Key successfully stored with name '{name}'.")

Generates a new key and stores it on a disk with the given name.

@key_app.command()
def regen( ctx: typer.models.Context, name: str, key_input: str, password: Optional[str] = None):
51@key_app.command()
52def regen(
53        ctx: Context, name: str, key_input: str, password: Optional[str] = None):
54    """
55    Stores the given key on a disk. Works with private key or mnemonic.
56    """
57    # TODO: secret input from env var and stdin
58    context = make_custom_context(ctx)
59    # Determine the input type based on the presence of spaces.
60    if re.search(r'\s', key_input):
61        # If mnemonic (contains spaces between words).
62        keypair = Keypair.create_from_mnemonic(key_input)
63        key_type = "mnemonic"
64    else:
65        # If private key (assumes no spaces).
66        keypair = Keypair.create_from_private_key(key_input, ss58_format=42)
67        key_type = "private key"
68        # Substrate does not return these.
69        keypair.mnemonic = ""  # type: ignore
70        keypair.seed_hex = ""
71
72    address = keypair.ss58_address
73    context.info(f"Loaded {key_type} with public address `{address}`.")
74
75    classic_store_key(keypair, name, password)
76
77    context.info(f"Key stored with name `{name}` successfully.")

Stores the given key on a disk. Works with private key or mnemonic.

@key_app.command()
def show( ctx: typer.models.Context, key: str, show_private: bool = False, password: Optional[str] = None):
80@key_app.command()
81def show(
82    ctx: Context, key: str, show_private: bool = False,
83    password: Optional[str] = None
84):
85    """
86    Show information about a key.
87    """
88    context = make_custom_context(ctx)
89
90    path = classic_key_path(key)
91    key_dict_json = try_load_key(path, context, password=password)
92    key_dict = json.loads(key_dict_json)
93
94    if show_private is not True:
95        key_dict["private_key"] = "[SENSITIVE-MODE]"
96        key_dict["seed_hex"] = "[SENSITIVE-MODE]"
97        key_dict["mnemonic"] = "[SENSITIVE-MODE]"
98
99    print_table_from_plain_dict(key_dict, ["Key", "Value"], context.console)

Show information about a key.

@key_app.command()
def balances( ctx: typer.models.Context, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, sort_balance: SortBalance = <SortBalance.all: 'all'>, use_universal_password: bool = <typer.models.OptionInfo object>):
102@key_app.command()
103def balances(
104    ctx: Context,
105    unit: BalanceUnit = BalanceUnit.joule,
106    sort_balance: SortBalance = SortBalance.all,
107    use_universal_password: bool = typer.Option(
108        False, help="""
109        If you want to use a password to decrypt all keys.
110        This will only work if all encrypted keys uses the same password.
111        If this is not the case, leave it blank and you will be prompted to give
112        every password.
113        """
114    )
115
116):
117    """
118    Gets balances of all keys.
119    """
120    context = make_custom_context(ctx)
121    client = context.com_client()
122    if use_universal_password:
123        universal_password = get_universal_password(context)
124    else:
125        universal_password = None
126
127    local_keys = local_key_addresses(context, universal_password=universal_password)
128    with context.console.status("Getting balances of all keys, this might take a while..."):
129        key2freebalance, key2stake = local_keys_allbalance(client, local_keys)
130    key_to_freebalance = {k: format_balance(
131        v, unit) for k, v in key2freebalance.items()}
132    key_to_stake = {k: format_balance(v, unit) for k, v in key2stake.items()}
133
134    key2balance = {k: v + key2stake[k] for k, v in key2freebalance.items()}
135    key_to_balance = {k: format_balance(v, unit)
136                      for k, v in key2balance.items()}
137
138    if sort_balance == SortBalance.all:
139        sorted_bal = {k: v for k, v in sorted(
140            key2balance.items(), key=lambda item: item[1], reverse=True)}
141    elif sort_balance == SortBalance.free:
142        sorted_bal = {k: v for k, v in sorted(
143            key2freebalance.items(), key=lambda item: item[1], reverse=True)}
144    elif sort_balance == SortBalance.staked:
145        sorted_bal = {k: v for k, v in sorted(
146            key2stake.items(), key=lambda item: item[1], reverse=True)}
147    else:
148        raise ValueError("Invalid sort balance option")
149
150    stake: list[str] = []
151    all_balance: list[str] = []
152    free: list[str] = []
153    keys: list[str] = []
154
155    for key, _ in sorted_bal.items():
156        keys.append(key)
157        free.append(key_to_freebalance[key])
158        stake.append(key_to_stake[key])
159        all_balance.append(key_to_balance[key])
160
161    pretty_dict = {
162        "key": keys,
163        "free": free,
164        "staked": stake,
165        "all": all_balance,
166    }
167
168    general_dict: dict[str, list[Any]] = cast(
169        dict[str, list[Any]], pretty_dict)
170    print_table_standardize(general_dict, context.console)

Gets balances of all keys.

@key_app.command(name='list')
def inventory( ctx: typer.models.Context, use_universal_password: bool = <typer.models.OptionInfo object>):
173@key_app.command(name='list')
174def inventory(
175    ctx: Context,
176    use_universal_password: bool = typer.Option(
177        False, help="""
178        Password to decrypt all keys.
179        This will only work if all encrypted keys uses the same password.
180        If this is not the case, leave it blank and you will be prompted to give
181        every password.
182        """
183    )
184):
185    """
186    Lists all keys stored on disk.
187    """
188    context = make_custom_context(ctx)
189    if use_universal_password:
190        universal_password = get_universal_password(context)
191    else:
192        universal_password = None
193    key_to_address = local_key_addresses(context, universal_password)
194    general_key_to_address: dict[str, str] = cast(
195        dict[str, str], key_to_address)
196    print_table_from_plain_dict(general_key_to_address, [
197                                "Key", "Address"], context.console)

Lists all keys stored on disk.

@key_app.command()
def stakefrom( ctx: typer.models.Context, key: str, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, password: Optional[str] = None):
200@key_app.command()
201def stakefrom(
202    ctx: Context, key: str,
203    unit: BalanceUnit = BalanceUnit.joule,
204    password: Optional[str] = None,
205):
206    """
207    Gets what keys is key staked from.
208    """
209    context = make_custom_context(ctx)
210    client = context.com_client()
211
212    if is_ss58_address(key):
213        key_address = key
214    else:
215        keypair = try_classic_load_key(key, context, password)
216        key_address = keypair.ss58_address
217        key_address = check_ss58_address(key_address)
218    with context.progress_status(f"Getting stake-from map for {key_address}..."):
219        result = client.get_stakefrom(key=key_address)
220
221    result = {k: format_balance(v, unit) for k, v in result.items()}
222
223    print_table_from_plain_dict(result, ["Key", "Stake"], context.console)

Gets what keys is key staked from.

@key_app.command()
def staketo( ctx: typer.models.Context, key: str, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, password: Optional[str] = None):
226@key_app.command()
227def staketo(
228    ctx: Context, key: str,
229    unit: BalanceUnit = BalanceUnit.joule,
230    password: Optional[str] = None,
231):
232    """
233    Gets stake to a key.
234    """
235    context = make_custom_context(ctx)
236    client = context.com_client()
237
238    if is_ss58_address(key):
239        key_address = key
240    else:
241        keypair = try_classic_load_key(key, context, password)
242        key_address = keypair.ss58_address
243        key_address = check_ss58_address(key_address)
244
245    with context.progress_status(f"Getting stake-to of {key_address}..."):
246        result = client.get_staketo(key=key_address)
247
248    result = {k: format_balance(v, unit) for k, v in result.items()}
249
250    print_table_from_plain_dict(result, ["Key", "Stake"], context.console)

Gets stake to a key.

@key_app.command()
def total_free_balance( ctx: typer.models.Context, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, use_universal_password: Optional[str] = <typer.models.OptionInfo object>):
253@key_app.command()
254def total_free_balance(
255    ctx: Context,
256    unit: BalanceUnit = BalanceUnit.joule,
257    use_universal_password: Optional[str] = typer.Option(
258        False, help="""
259        Password to decrypt all keys.
260        This will only work if all encrypted keys uses the same password.
261        If this is not the case, leave it blank and you will be prompted to give
262        every password.
263        """
264    )
265):
266    """
267    Returns total balance of all keys on a disk
268    """
269    context = make_custom_context(ctx)
270    client = context.com_client()
271
272    if use_universal_password:
273        universal_password = get_universal_password(context)
274    else:
275        universal_password = None
276    local_keys = local_key_addresses(context, universal_password)
277    with context.progress_status("Getting total free balance of all keys..."):
278        key2balance: dict[str, int] = local_keys_to_freebalance(client, local_keys)
279
280        balance_sum = sum(key2balance.values())
281
282        context.output(format_balance(balance_sum, unit=unit))

Returns total balance of all keys on a disk

@key_app.command()
def total_staked_balance( ctx: typer.models.Context, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, use_universal_password: bool = <typer.models.OptionInfo object>):
285@key_app.command()
286def total_staked_balance(
287    ctx: Context, unit: BalanceUnit = BalanceUnit.joule,
288    use_universal_password: bool = typer.Option(
289        False, help="""
290    Password to decrypt all keys.
291    This will only work if all encrypted keys uses the same password.
292    If this is not the case, leave it blank and you will be prompted to give
293    every password.
294    """
295    )
296):
297    """
298    Returns total stake of all keys on a disk
299    """
300    context = make_custom_context(ctx)
301    client = context.com_client()
302
303    if use_universal_password:
304        universal_password = get_universal_password(context)
305    else:
306        universal_password = None
307    local_keys = local_key_addresses(context, universal_password)
308    with context.progress_status("Getting total staked balance of all keys..."):
309        key2stake: dict[str, int] = local_keys_to_stakedbalance(
310            client, local_keys,
311        )
312
313        stake_sum = sum(key2stake.values())
314
315        context.output(format_balance(stake_sum, unit=unit))

Returns total stake of all keys on a disk

@key_app.command()
def total_balance( ctx: typer.models.Context, unit: communex._common.BalanceUnit = <BalanceUnit.joule: 'joule'>, use_universal_password: bool = <typer.models.OptionInfo object>):
318@key_app.command()
319def total_balance(
320    ctx: Context, unit: BalanceUnit = BalanceUnit.joule,
321    use_universal_password: bool = typer.Option(
322        False, help="""
323    Password to decrypt all keys.
324    This will only work if all encrypted keys uses the same password.
325    If this is not the case, leave it blank and you will be prompted to give
326    every password.
327    """
328    )
329):
330    """
331    Returns total tokens of all keys on a disk
332    """
333    context = make_custom_context(ctx)
334    client = context.com_client()
335
336    if use_universal_password:
337        universal_password = get_universal_password(context)
338    else:
339        universal_password = None
340    local_keys = local_key_addresses(context, universal_password)
341    with context.progress_status("Getting total tokens of all keys..."):
342        key2balance, key2stake = local_keys_allbalance(
343            client, local_keys
344        )
345        key2tokens = {k: v + key2stake[k] for k, v in key2balance.items()}
346        tokens_sum = sum(key2tokens.values())
347
348        context.output(format_balance(tokens_sum, unit=unit))

Returns total tokens of all keys on a disk

@key_app.command()
def power_delegation( ctx: typer.models.Context, key: Optional[str] = None, enable: bool = <typer.models.OptionInfo object>, use_universal_password: bool = <typer.models.OptionInfo object>):
351@key_app.command()
352def power_delegation(
353    ctx: Context,
354    key: Optional[str] = None,
355    enable: bool = typer.Option(True, "--disable"),
356    use_universal_password: bool = typer.Option(False)
357):
358    """
359    Gets power delegation of a key.
360    """
361    context = make_custom_context(ctx)
362    client = context.com_client()
363    if key is None:
364        action = "enable" if enable else "disable"
365        confirm_message = (
366            f"Key was not set, this will {action} vote power delegation for all"
367            " keys on disk. Do you want to proceed?"
368        )
369        if not typer.confirm(confirm_message):
370            context.info("Aborted.")
371            exit(0)
372
373        if use_universal_password:
374            universal_password = get_universal_password(context)
375        else:
376            universal_password = None
377        local_keys = local_key_addresses(context, universal_password)
378    else:
379        local_keys = {key: None}
380    for key_name in local_keys.keys():
381        keypair = try_classic_load_key(key_name, context)
382        if enable is True:
383            context.info(f"Enabling vote power delegation on key {key_name} ...")
384            client.enable_vote_power_delegation(keypair)
385        else:
386            context.info(f"Disabling vote power delegation on key {key_name} ...")
387            client.disable_vote_power_delegation(keypair)

Gets power delegation of a key.