Edit on GitHub

communex.cli.key

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

Gets balances of all keys.

@key_app.command(name='list')
def inventory(ctx: typer.models.Context):
184@key_app.command(name="list")
185def inventory(
186    ctx: Context,
187):
188    """
189    Lists all keys stored on disk.
190    """
191    context = make_custom_context(ctx)
192
193    key_to_address = local_key_addresses(context.password_manager)
194    general_key_to_address: dict[str, str] = cast(
195        dict[str, str], key_to_address
196    )
197    print_table_from_plain_dict(
198        general_key_to_address, ["Key", "Address"], context.console
199    )

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):
202@key_app.command()
203def stakefrom(
204    ctx: Context,
205    key: str,
206    unit: BalanceUnit = BalanceUnit.joule,
207    password: Optional[str] = None,
208):
209    """
210    Gets what keys is key staked from.
211    """
212    context = make_custom_context(ctx)
213    client = context.com_client()
214
215    if is_ss58_address(key):
216        key_address = key
217    else:
218        keypair = context.load_key(key, password)
219        key_address = keypair.ss58_address
220        key_address = check_ss58_address(key_address)
221    with context.progress_status(
222        f"Getting stake-from map for {key_address}..."
223    ):
224        result = client.get_stakefrom(key=key_address)
225
226    result = {k: format_balance(v, unit) for k, v in result.items()}
227
228    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):
231@key_app.command()
232def staketo(
233    ctx: Context,
234    key: str,
235    unit: BalanceUnit = BalanceUnit.joule,
236    password: Optional[str] = None,
237):
238    """
239    Gets stake to a key.
240    """
241    context = make_custom_context(ctx)
242    client = context.com_client()
243
244    if is_ss58_address(key):
245        key_address = key
246    else:
247        keypair = context.load_key(key, password)
248        key_address = keypair.ss58_address
249        key_address = check_ss58_address(key_address)
250
251    with context.progress_status(f"Getting stake-to of {key_address}..."):
252        result = client.get_staketo(key=key_address)
253
254    result = {k: format_balance(v, unit) for k, v in result.items()}
255
256    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>):
259@key_app.command()
260def total_free_balance(
261    ctx: Context,
262    unit: BalanceUnit = BalanceUnit.joule,
263    use_universal_password: Optional[str] = typer.Option(
264        False,
265        help="""
266        Password to decrypt all keys.
267        This will only work if all encrypted keys uses the same password.
268        If this is not the case, leave it blank and you will be prompted to give
269        every password.
270        """,
271    ),
272):
273    """
274    Returns total balance of all keys on a disk
275    """
276    context = make_custom_context(ctx)
277    client = context.com_client()
278
279    local_keys = local_key_addresses(context.password_manager)
280    with context.progress_status("Getting total free balance of all keys..."):
281        key2balance: dict[str, int] = local_keys_to_freebalance(
282            client, local_keys
283        )
284
285        balance_sum = sum(key2balance.values())
286
287        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>):
290@key_app.command()
291def total_staked_balance(
292    ctx: Context,
293    unit: BalanceUnit = BalanceUnit.joule,
294    use_universal_password: bool = typer.Option(
295        False,
296        help="""
297    Password to decrypt all keys.
298    This will only work if all encrypted keys uses the same password.
299    If this is not the case, leave it blank and you will be prompted to give
300    every password.
301    """,
302    ),
303):
304    """
305    Returns total stake of all keys on a disk
306    """
307    context = make_custom_context(ctx)
308    client = context.com_client()
309
310    local_keys = local_key_addresses(context.password_manager)
311    with context.progress_status("Getting total staked balance of all keys..."):
312        key2stake: dict[str, int] = local_keys_to_stakedbalance(
313            client,
314            local_keys,
315        )
316
317        stake_sum = sum(key2stake.values())
318
319        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>):
322@key_app.command()
323def total_balance(
324    ctx: Context,
325    unit: BalanceUnit = BalanceUnit.joule,
326    use_universal_password: bool = typer.Option(
327        False,
328        help="""
329    Password to decrypt all keys.
330    This will only work if all encrypted keys uses the same password.
331    If this is not the case, leave it blank and you will be prompted to give
332    every password.
333    """,
334    ),
335):
336    """
337    Returns total tokens of all keys on a disk
338    """
339    context = make_custom_context(ctx)
340    client = context.com_client()
341
342    local_keys = local_key_addresses(context.password_manager)
343    with context.progress_status("Getting total tokens of all keys..."):
344        key2balance, key2stake = local_keys_allbalance(client, local_keys)
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>):
351@key_app.command()
352def power_delegation(
353    ctx: Context,
354    key: Optional[str] = None,
355    enable: bool = typer.Option(True, "--disable"),
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        local_keys = local_key_addresses(context.password_manager)
373    else:
374        local_keys = {key: None}
375    for key_name in local_keys.keys():
376        keypair = context.load_key(key_name, None)
377        if enable is True:
378            context.info(
379                f"Enabling vote power delegation on key {key_name} ..."
380            )
381            client.enable_vote_power_delegation(keypair)
382        else:
383            context.info(
384                f"Disabling vote power delegation on key {key_name} ..."
385            )
386            client.disable_vote_power_delegation(keypair)

Gets power delegation of a key.