From e893f2e8f95584ddc236be6f743f0705287aa182 Mon Sep 17 00:00:00 2001 From: "Frank V. Castellucci" Date: Mon, 15 Jul 2024 05:27:00 -0400 Subject: [PATCH] Added 'new_keypair' to PysuiConfiguration' --- pysui/sui/sui_pgql/config/confgroup.py | 74 +++++++++- pysui/sui/sui_pgql/config/confmodel.py | 2 +- pysui/sui/sui_pgql/config/pysui_config.py | 163 +++++++++++++++++++-- pysui/sui/sui_pgql/pgql_txb_signing.py | 2 +- pysui/sui/sui_pgql/pgql_validators.py | 3 +- samples/cmdsg.py | 164 +++++++++++++--------- 6 files changed, 323 insertions(+), 85 deletions(-) diff --git a/pysui/sui/sui_pgql/config/confgroup.py b/pysui/sui/sui_pgql/config/confgroup.py index 81f9c11..2aacdec 100644 --- a/pysui/sui/sui_pgql/config/confgroup.py +++ b/pysui/sui/sui_pgql/config/confgroup.py @@ -5,10 +5,12 @@ """Sui Configuration Group.""" +import base64 +import hashlib import dataclasses from typing import Optional, Union import dataclasses_json -from pysui.sui.sui_crypto import keypair_from_keystring, SuiKeyPair +import pysui.sui.sui_crypto as crypto @dataclasses.dataclass @@ -64,6 +66,13 @@ def _address_exists(self, *, address: str) -> Union[str, bool]: """Check if address is valid.""" return next(filter(lambda addy: addy == address, self.address_list), False) + def _key_exists(self, *, key_string: str) -> Union[ProfileKey, bool]: + """Check if key string exists.""" + return next( + filter(lambda pkey: pkey.private_key_base64 == key_string, self.key_list), + False, + ) + @property def active_address(self) -> str: """Return the active address.""" @@ -93,7 +102,7 @@ def active_alias(self, change_to: str) -> str: return _res.alias raise ValueError(f"Alias {change_to} not found in group") - def address_for_alias(self, alias: str) -> str: + def address_for_alias(self, *, alias: str) -> str: """Get address associated with alias.""" _res = self._alias_exists(alias_name=alias) if _res: @@ -101,7 +110,7 @@ def address_for_alias(self, alias: str) -> str: return self.address_list[aliindx] raise ValueError(f"Alias {alias} not found in group") - def alias_for_address(self, address: str) -> ProfileAlias: + def alias_for_address(self, *, address: str) -> ProfileAlias: """Get alias associated with address.""" _res = self._address_exists(address=address) if _res: @@ -109,7 +118,7 @@ def alias_for_address(self, address: str) -> ProfileAlias: return self.alias_list[adindex] raise ValueError(f"Address {address} not found in group") - def alias_name_for_address(self, address: str) -> str: + def alias_name_for_address(self, *, address: str) -> str: """Get alias associated with address.""" _res = self._address_exists(address=address) if _res: @@ -117,6 +126,18 @@ def alias_name_for_address(self, address: str) -> str: return self.alias_list[adindex].alias raise ValueError(f"Address {address} not found in group") + def replace_alias_name(self, *, from_alias: str, to_alias: str) -> str: + """Replace alias name and return associated address.""" + _res = self._alias_exists(alias_name=from_alias) + if _res: + _rese = self._alias_exists(alias_name=to_alias) + if not _rese: + aliindx = self.alias_list.index(_res) + _res.alias = to_alias + return self.address_list[aliindx] + raise ValueError(f"Alias {to_alias} already exists") + raise ValueError(f"Alias {from_alias} not found in group") + @property def active_profile(self) -> Profile: """Gets the active profile.""" @@ -135,14 +156,53 @@ def active_profile(self, change_to: str) -> Profile: return _res raise ValueError(f"{change_to} profile does not exist") - def address_keypair(self, address: str) -> SuiKeyPair: - """.""" + def address_keypair(self, *, address: str) -> crypto.SuiKeyPair: + """Fetch an addresses KeyPair.""" _res = self._address_exists(address=address) if _res: - return keypair_from_keystring( + return crypto.keypair_from_keystring( self.key_list[self.address_list.index(_res)].private_key_base64 ) + def add_keypair_and_parts( + self, + *, + new_alias: str, + new_keypair: crypto.SuiKeyPair, + make_active: Optional[bool] = False, + ) -> str: + """Add a new keypair with associated address and alias.""" + _new_keystr = new_keypair.serialize() + if not self._key_exists(key_string=_new_keystr): + if self._alias_exists(alias_name=new_alias): + raise ValueError( + f"Alias {new_alias} already exist attempting new key and address." + ) + # ProfileKey Entry + _prvk_entry = ProfileKey(_new_keystr) + # Generate artifacts + _pkey_bytes = new_keypair.to_bytes() + _digest = _pkey_bytes[0:33] if _pkey_bytes[0] == 0 else _pkey_bytes[0:34] + # ProfileAlias Entry + _alias_entry = ProfileAlias( + new_alias, + base64.b64encode(new_keypair.public_key.scheme_and_key()).decode(), + ) + _new_addy = format( + f"0x{hashlib.blake2b(_digest, digest_size=32).hexdigest()}" + ) + # Populate group + self.address_list.append(_new_addy) + self.key_list.append(_prvk_entry) + self.alias_list.append(_alias_entry) + + if make_active: + self.using_address = _new_addy + return _new_addy + raise ValueError( + f"Private keystring {_new_keystr} already exists attempting new key and address.." + ) + def add_profile(self, *, new_prf: Profile, make_active: bool = False): """Add profile to list after validating name""" _res = self._profile_exists(profile_name=new_prf.profile_name) diff --git a/pysui/sui/sui_pgql/config/confmodel.py b/pysui/sui/sui_pgql/config/confmodel.py index 08cc2b2..f712b19 100644 --- a/pysui/sui/sui_pgql/config/confmodel.py +++ b/pysui/sui/sui_pgql/config/confmodel.py @@ -51,7 +51,7 @@ def remove_group(self, *, group_name: str) -> None: self.group_active = self.groups[0].group_name def get_group(self, *, group_name: str) -> prfgrp.ProfileGroup: - """Test for group existence.""" + """Get a group or throw exception if doesn't exist.""" _res = self._group_exists(group_name=group_name) if not _res: raise ValueError(f"{group_name} does not exist.") diff --git a/pysui/sui/sui_pgql/config/pysui_config.py b/pysui/sui/sui_pgql/config/pysui_config.py index a68ecc4..e6493e5 100644 --- a/pysui/sui/sui_pgql/config/pysui_config.py +++ b/pysui/sui/sui_pgql/config/pysui_config.py @@ -10,6 +10,13 @@ from pathlib import Path from typing import Optional +import pysui.sui.sui_crypto as crypto +import pysui.sui.sui_utils as utils + +from pysui.abstracts.client_keypair import SignatureScheme +from pysui.sui.sui_constants import SUI_MAX_ALIAS_LEN, SUI_MIN_ALIAS_LEN + +from pysui.sui.sui_pgql.config.confgroup import ProfileGroup from pysui.sui.sui_pgql.config.confmodel import PysuiConfigModel @@ -53,6 +60,10 @@ def __init__( _bcfg = Path("~/.cargo/bin/sui").expanduser() # Initialize from sui config if found self.refresh_legacy(replace=False) + # Set if not exist, don't overwrite + self.model.add_group( + group=ProfileGroup("user", "", "", [], [], [], []), make_active=False + ) if self._model.initialize_gql_rpc( sui_binary=_bcfg, gql_rpc_group_name=self.SUI_GQL_RPC_GROUP, @@ -105,42 +116,72 @@ def model(self) -> PysuiConfigModel: """.""" return self._model + @property + def active_group(self) -> ProfileGroup: + """Return the active group.""" + return self._model.active_group + @property def active_address(self) -> str: """Returns the active groups active address.""" - return self._model.active_group.using_address + return self.active_group.using_address @property def active_address_alias(self) -> str: """Returns the active groups active address.""" - return self._model.active_group.active_alias + return self.active_group.active_alias @property def active_env(self) -> str: """Returns the active groups active profile name.""" - return self._model.active_group.using_profile + return self.active_group.using_profile @property def url(self) -> str: """Returns the active groups active profile url.""" - return self._model.active_group.active_profile.url + return self.active_group.active_profile.url @property def faucet_url(self) -> str: """Returns the active groups active profile faucet url.""" - return self._model.active_group.active_profile.faucet_urls + return self.active_group.active_profile.faucet_urls @property def faucet_status_url(self) -> str: """Returns the active groups active profile faucet status url.""" - return self._model.active_group.active_profile.faucet_status_url + return self.active_group.active_profile.faucet_status_url @property def config_path(self) -> str: """Return configuration breadcrump path.""" - _group = self._model.active_group + _group = self.active_group return f"{_group.group_name}.{_group.active_profile.profile_name}" + def address_for_alias(self, *, alias_name: str) -> str: + """Return the address for the alias name in current group.""" + return self.active_group.address_for_alias(alias=alias_name) + + def alias_for_address(self, *, address: str) -> str: + """Return the alias for the address in current group.""" + return self.active_group.alias_for_address(address=address) + + def rename_alias( + self, + *, + existing_alias: str, + new_alias: str, + in_group: Optional[str] = None, + persist: Optional[bool] = False, + ) -> str: + """Rename an alias in a group, default to active_group.""" + _group = self.active_group + if in_group and in_group != _group.group_name: + _group = self._model.get_group(group_name=in_group) + + _res = _group.replace_alias_name(from_alias=existing_alias, to_alias=new_alias) + if _res and persist: + self._write_model() + def make_active( self, *, @@ -163,7 +204,7 @@ def make_active( else: raise ValueError(f"{group_name} does not exist") else: - _group = self._model.active_group + _group = self.active_group # Change profile, checks if it exists if profile_name and _group.using_profile != profile_name: @@ -181,3 +222,109 @@ def make_active( if _changes and persist: self._write_model() + + def _alias_check_or_gen( + self, + *, + aliases: Optional[list[str]] = None, + word_counts: Optional[int] = 12, + alias: Optional[str] = None, + current_iter: Optional[int] = 0, + ) -> str: + """_alias_check_or_gen If alias is provided, checks if unique otherwise creates one or more. + + :param aliases: List of existing aliases, defaults to None + :type aliases: list[str], optional + :param word_counts: Words count used for mnemonic phrase, defaults to 12 + :type word_counts: Optional[int], optional + :param alias: An inbound alias, defaults to None + :type alias: Optional[str], optional + :param current_iter: Internal recursion count, defaults to 0 + :type current_iter: Optional[int], optional + :return: An aliases + :rtype: str + """ + if not alias: + parts = list( + utils.partition( + crypto.gen_mnemonic_phrase(word_counts).split(" "), + int(word_counts / 2), + ) + ) + alias_names = [k + "-" + v for k, v in zip(*parts)] + + # alias_names = self._alias_gen_batch(word_counts=word_counts) + # Find a unique part if just_one + if not aliases: + alias = alias_names[0] + else: + for alias_name in alias_names: + if alias_name not in aliases: + # Found one + alias = alias_name + break + # If all match (unlikely), try unless threshold + if not alias: + if current_iter > 2: + raise ValueError("Unable to find unique alias") + else: + alias = self._alias_check_or_gen( + aliases=aliases, + word_counts=word_counts, + current_iter=current_iter + 1, + ) + else: + if alias in aliases: + raise ValueError(f"Alias {alias} already exists.") + if not (SUI_MIN_ALIAS_LEN <= len(alias) <= SUI_MAX_ALIAS_LEN): + raise ValueError( + f"Invalid alias string length, must be betwee {SUI_MIN_ALIAS_LEN} and {SUI_MAX_ALIAS_LEN} characters." + ) + return alias + + def new_keypair( + self, + *, + of_keytype: SignatureScheme, + in_group: Optional[str] = None, + word_counts: Optional[int] = 12, + derivation_path: Optional[str] = None, + make_active: Optional[bool] = False, + alias: Optional[str] = None, + persist: Optional[bool] = True, + ) -> tuple[str, str]: + """Creates a new keypair returning generated passphrase and associated address.""" + # Resolve group + _group = self.active_group + if in_group and in_group != _group.group_name: + _group = self._model.get_group(group_name=in_group) + + match of_keytype: + case ( + SignatureScheme.ED25519 + | SignatureScheme.SECP256K1 + | SignatureScheme.SECP256R1 + ): + # First, early check alias given or create new one + _aliases = [x.alias for x in _group.alias_list] + alias = self._alias_check_or_gen( + aliases=_aliases, alias=alias, word_counts=word_counts + ) + # Generate the new key and address + mnem, keypair = crypto.create_new_keypair( + scheme=of_keytype, + word_counts=word_counts, + derv_path=derivation_path, + ) + new_addy = _group.add_keypair_and_parts( + new_alias=alias, new_keypair=keypair, make_active=make_active + ) + if make_active and _group.group_name != self.active_group.group_name: + self._model.active_group = _group.group_name + if persist: + self._write_model() + return mnem, new_addy + case _: + raise NotImplementedError( + f"{of_keytype}: Not recognized as valid keypair scheme." + ) diff --git a/pysui/sui/sui_pgql/pgql_txb_signing.py b/pysui/sui/sui_pgql/pgql_txb_signing.py index ac2887f..674168b 100644 --- a/pysui/sui/sui_pgql/pgql_txb_signing.py +++ b/pysui/sui/sui_pgql/pgql_txb_signing.py @@ -130,7 +130,7 @@ def get_signatures(self, *, config: PysuiConfiguration, tx_bytes: str) -> list[s if isinstance(signer, str): sig_list.append( - config.model.active_group.address_keypair(signer).new_sign_secure( + config.active_group.address_keypair(address=signer).new_sign_secure( tx_bytes ) ) diff --git a/pysui/sui/sui_pgql/pgql_validators.py b/pysui/sui/sui_pgql/pgql_validators.py index 3608f77..d5b1d68 100644 --- a/pysui/sui/sui_pgql/pgql_validators.py +++ b/pysui/sui/sui_pgql/pgql_validators.py @@ -42,8 +42,7 @@ def check_owner( inlen = len(owner) assert isinstance(owner, str), "Owner should be str" try: - return config.model.active_group.address_for_alias(owner) - # return config.addr4al(owner).address + return config.address_for_alias(alias_name=owner) except ValueError: pass if inlen < 3 or inlen > SUI_HEX_ADDRESS_STRING_LEN: diff --git a/samples/cmdsg.py b/samples/cmdsg.py index d8c743a..dd60d78 100644 --- a/samples/cmdsg.py +++ b/samples/cmdsg.py @@ -10,8 +10,9 @@ from pathlib import Path import pprint import sys -from pysui import __version__, SuiRpcResult, SuiAddress, PysuiConfiguration +from pysui import __version__, SuiRpcResult +from pysui.sui.sui_pgql.config.pysui_config import PysuiConfiguration from pysui.sui.sui_pgql.pgql_clients import SuiGQLClient from pysui.sui.sui_pgql.pgql_sync_txn import SuiTransaction import pysui.sui.sui_pgql.pgql_query as qn @@ -26,6 +27,14 @@ SuiMiisingModuleByteCode, ) +_SUI_COIN_TYPE: str = ( + "0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI>" +) + +_SUI_UPGRADE_CAP: str = ( + "0x0000000000000000000000000000000000000000000000000000000000000002::package::UpgradeCap" +) + def handle_result(result: SuiRpcResult) -> SuiRpcResult: """.""" @@ -65,7 +74,7 @@ def transaction_execute( def sdk_version(_client: SuiGQLClient, _args: argparse.Namespace) -> None: """Dispay version(s).""" print( - f"pysui SDK version: {__version__} SUI GraphQL Schema version {_client.schema_version}" + f"pysui SDK version: {__version__} SUI GraphQL Schema version {_client.schema_version()}" ) @@ -92,16 +101,14 @@ def sui_addresses(client: SuiGQLClient, args: argparse.Namespace) -> None: for _ in range(0, len(header_str)): print("-", end="") print() - pcfg: PysuiConfiguration = client.config - pgrp = pcfg.model.active_group - for addy in pgrp.address_list: + for addy in client.config.active_group.address_list: addyl = addy - if addy == pcfg.active_address: + if addy == client.config.active_address: addyl = f"{active:^3s}{addyl:^72s}" else: addyl = f"{space:^3s}{addyl:^72s}" if args.details: - palias = pgrp.alias_for_address(addy) + palias = client.config.alias_for_address(address=addy) addyl = addyl + f" {palias.public_key_base64:54s} {palias.alias}" print(addyl) @@ -136,8 +143,7 @@ def _detail_gas_objects(gas_objects: list[ptypes.SuiCoinObjectGQL]) -> None: if args.owner: for_owner = args.owner.address elif args.alias: - pcfg: PysuiConfiguration = client.config - for_owner = pcfg.model.active_group.address_for_alias(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) else: for_owner = client.config.active_address @@ -164,26 +170,27 @@ def _detail_gas_objects(gas_objects: list[ptypes.SuiCoinObjectGQL]) -> None: def sui_new_address(client: SuiGQLClient, args: argparse.Namespace) -> None: """Generate a new SUI address.""" + _config: PysuiConfiguration = client.config if args.ed25519: - mnen, address = client.config.create_new_keypair_and_address( - scheme=SignatureScheme.ED25519, alias=args.alias + mnen, address = _config.new_keypair( + of_keytype=SignatureScheme.ED25519, alias=args.alias, persist=True ) elif args.secp256k1: - mnen, address = client.config.create_new_keypair_and_address( - scheme=SignatureScheme.SECP256K1, alias=args.alias + mnen, address = _config.new_keypair( + of_keytype=SignatureScheme.SECP256K1, alias=args.alias, persist=True ) elif args.secp256r1: - mnen, address = client.config.create_new_keypair_and_address( - scheme=SignatureScheme.SECP256R1, alias=args.alias + mnen, address = _config.new_keypair( + of_keytype=SignatureScheme.SECP256R1, alias=args.alias, persist=True ) else: raise ValueError(f"Unknown keytype {args}") print(f"Keep this passphrase: '{mnen}'") print(f"For new address: {address}") if args.alias: - print(f"Alias assigned {client.config.al4addr(address)}") + print(f"Alias assigned {_config.alias_for_address(address=address)}") else: - print(f"Alias generated {client.config.al4addr(address)}") + print(f"Alias generated {_config.alias_for_address(address=address)}") def sui_package(client: SuiGQLClient, args: argparse.Namespace) -> None: @@ -246,22 +253,22 @@ def sui_objects(client: SuiGQLClient, args: argparse.Namespace) -> None: """Show specific Sui object.""" for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) else: for_owner = client.config.active_address all_objects = [] result = client.execute_query_node( - with_node=qn.GetObjectsOwnedByAddress(owner=for_owner.address) + with_node=qn.GetObjectsOwnedByAddress(owner=for_owner) ) while result.is_ok(): all_objects.extend(result.result_data.data) if result.result_data.next_cursor.hasNextPage: result = client.execute_query_node( with_node=qn.GetObjectsOwnedByAddress( - owner=for_owner.address, + owner=for_owner, next_page=result.result_data.next_cursor, ) ) @@ -275,8 +282,14 @@ def sui_objects(client: SuiGQLClient, args: argparse.Namespace) -> None: else: _objects_header_print() for desc in all_objects: + if desc.object_type == _SUI_COIN_TYPE: + dobj_type = "Sui Coin" + elif desc.object_type == _SUI_UPGRADE_CAP: + dobj_type = "Upgrade Cap" + else: + dobj_type = desc.object_type print( - f"{desc.object_id} | {desc.version:^8} | {desc.object_digest} | {desc.object_type}" + f"{desc.object_id} | {desc.version:^8} | {desc.object_digest} | {dobj_type}" ) else: print(f"{result.result_string}") @@ -290,13 +303,15 @@ def transfer_object(client: SuiGQLClient, args: argparse.Namespace) -> None: :param args: _description_ :type args: argparse.Namespace """ - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) txn.transfer_objects( transfers=[args.transfer.value], recipient=args.recipient.address ) @@ -310,12 +325,15 @@ def transfer_object(client: SuiGQLClient, args: argparse.Namespace) -> None: def transfer_sui(client: SuiGQLClient, args: argparse.Namespace) -> None: """Transfer gas object.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address + + txn = SuiTransaction(client=client, initial_sender=for_owner) txn.transfer_sui( recipient=args.recipient.address, from_coin=args.takes.value, @@ -330,13 +348,15 @@ def transfer_sui(client: SuiGQLClient, args: argparse.Namespace) -> None: def merge_coin(client: SuiGQLClient, args: argparse.Namespace) -> None: """Merge two coins together.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address # print(args) - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) txn.merge_coins( merge_to=args.primary_coin.value, merge_from=[args.coin_to_merge.value] ) @@ -349,21 +369,23 @@ def merge_coin(client: SuiGQLClient, args: argparse.Namespace) -> None: def split_coin(client: SuiGQLClient, args: argparse.Namespace) -> None: """Split coin into amounts.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address # print(args) - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) coins = txn.split_coin( coin=args.coin_object_id.value, amounts=[int(x) for x in args.mists] ) if not isinstance(coins, list): - txn.transfer_objects(transfers=[coins], recipient=for_owner.address) + txn.transfer_objects(transfers=[coins], recipient=for_owner) else: - txn.transfer_objects(transfers=coins, recipient=for_owner.address) + txn.transfer_objects(transfers=coins, recipient=for_owner) transaction_execute( txb=txn, gas_budget=args.budget, @@ -373,14 +395,16 @@ def split_coin(client: SuiGQLClient, args: argparse.Namespace) -> None: def split_coin_equally(client: SuiGQLClient, args: argparse.Namespace) -> None: """Split coin equally across counts.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address # print(args) - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) txn.split_coin_equal( coin=args.coin_object_id.value, split_count=int(args.split_count) ) @@ -393,16 +417,18 @@ def split_coin_equally(client: SuiGQLClient, args: argparse.Namespace) -> None: def publish(client: SuiGQLClient, args: argparse.Namespace) -> None: """Publish a sui package.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address # print(args) try: - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) upc = txn.publish(project_path=args.package) - txn.transfer_objects(transfers=[upc], recipient=for_owner.address) + txn.transfer_objects(transfers=[upc], recipient=for_owner) transaction_execute( txb=txn, gas_budget=args.budget, @@ -418,11 +444,13 @@ def publish(client: SuiGQLClient, args: argparse.Namespace) -> None: def move_call(client: SuiGQLClient, args: argparse.Namespace) -> None: """Invoke a Sui move smart contract function.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address target = ( args.package_object_id.value @@ -445,7 +473,7 @@ def move_call(client: SuiGQLClient, args: argparse.Namespace) -> None: else: type_arguments = [] - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) res = txn.move_call( target=target, arguments=arguments, type_arguments=type_arguments ) @@ -458,13 +486,15 @@ def move_call(client: SuiGQLClient, args: argparse.Namespace) -> None: def sui_pay(client: SuiGQLClient, args: argparse.Namespace) -> None: """Payments for one or more recipients from one or more coins for one or more amounts.""" - for_owner: SuiAddress = client.config.active_address + for_owner: str = None if args.owner: - for_owner = args.owner + for_owner = args.owner.address elif args.alias: - for_owner = client.config.addr4al(args.alias) + for_owner = client.config.address_for_alias(alias_name=args.alias) + else: + for_owner = client.config.active_address - txn = SuiTransaction(client=client, initial_sender=for_owner.address) + txn = SuiTransaction(client=client, initial_sender=for_owner) if len(args.input_coins) == len(args.mists) == len(args.recipients): for x in range(len(args.input_coins)): i_coin = txn.split_coin( @@ -552,25 +582,27 @@ def txn_txn(client: SuiGQLClient, args: argparse.Namespace) -> None: def alias_list(client: SuiGQLClient, args: argparse.Namespace) -> None: """List address aliases.""" print() - for alias in client.config.aliases: - print(f"Alias: {alias}") - print(f"Address: {client.config.addr4al(alias)}") - print(f"PublicKey: {client.config.pk4al(alias)}\n") + agroup = client.config.active_group + for alias_obj in agroup.alias_list: + print(f"Alias: {alias_obj.alias}") + print(f"Address: {agroup.address_for_alias(alias_obj.alias)}") + print(f"PublicKey: {alias_obj.public_key_base64}\n") def alias_rename(client: SuiGQLClient, args: argparse.Namespace) -> None: """List address aliases.""" print() try: - client.config.addr4al(args.existing) + client.config.address_for_alias(args.existing) except ValueError as ve: print(ve.args) return try: - client.config.rename_alias(old_alias=args.existing, new_alias=args.to) - print( - f"Renamed {args.existing} to {args.to} for Sui address {client.config.addr4al(args.to)}" + + assoc_addy = client.config.rename_alias( + existing_alias=args.existing, new_alias=args.to, persist=True ) + print(f"Renamed {args.existing} to {args.to} for Sui address {assoc_addy}") except ValueError as ve: print(ve.args) @@ -581,7 +613,7 @@ def alias_rename(client: SuiGQLClient, args: argparse.Namespace) -> None: "active-address": sui_active_address, "addresses": sui_addresses, "gas": sui_gas, - "new-address": sui_new_address, + "new-address": sui_new_address, # TODO: Fix "object": sui_object, "objects": sui_objects, "package": sui_package,