Skip to content
This repository has been archived by the owner on Jan 3, 2025. It is now read-only.

Commit

Permalink
Gadgets to Sui GraphQL. References #14
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankC01 committed Apr 30, 2024
1 parent 769d4f2 commit 5f74c9a
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 137 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added address alias `-a, --alias` support in command line arguments
- Added `-s, --self-recipient` support in command line arguments (used to be `-a, --address-owner`)
- Removed `-m, --merge-threshold` from command line arguments
- BREAKING for `to-one`:
- Changed '-a,--address` to `-o,--owner`
- Added address alias `-a, --alias` support
- Added `-i,--inspect` to print PTB and dry run the transaction
- Removed `-m, --merge-threshold` from command line arguments



## [0.4.3] - 2023-11-06

Expand Down
93 changes: 13 additions & 80 deletions pysui_gadgets/splay/splay.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,14 @@
"""Splay - Splits coins evenly across addresses."""

import sys
import base64
from typing import Optional
from pysui import SuiConfig, SuiAddress
from pysui import SuiConfig
from pysui_gadgets.utils.cmdlines import splay_parser
from pysui.sui.sui_pgql.pgql_clients import SuiGQLClient
from pysui.sui.sui_pgql.pgql_sync_txn import SuiTransaction
import pysui_gadgets.utils.exec_helpers as utils
import pysui.sui.sui_pgql.pgql_types as pgql_type
import pysui.sui.sui_pgql.pgql_query as qn


def _resolve_owner(config: SuiConfig, owner, alias):
"""Resolve the parsers directive of coin ownership."""
owner_designate: SuiAddress = None
if owner:
owner_designate: SuiAddress = owner
elif alias:
owner_designate: SuiAddress = config.addr4al(alias)
else:
raise ValueError(f"Owner not designated.")

if owner_designate != config.active_address:
if owner_designate.address not in config.addresses:
raise ValueError(f"Missing private key for {owner_designate.address}")
print(f"Setting coin owner to {owner_designate.address}")
config.set_active_address(owner_designate)


def _get_all_owner_gas(client: SuiGQLClient) -> list[pgql_type.SuiCoinObjectGQL]:
"""Retreive all owners Gas Objects."""
coin_list: list[pgql_type.SuiCoinObjectGQL] = []
owner: str = client.config.active_address.address
result = client.execute_query_node(with_node=qn.GetCoins(owner=owner))
while True:
if result.is_ok():
coin_list.extend(result.result_data.data)
if result.result_data.next_cursor.hasNextPage:
result = client.execute_query_node(
with_node=qn.GetCoins(
owner=owner, next_page=result.result_data.next_cursor
)
)
else:
break
else:
raise ValueError(f"GetCoins error {result.result_string}")
if not coin_list:
raise ValueError(f"{owner} has no Sui coins to splay")
return coin_list


def _consolidate_coin(
Expand Down Expand Up @@ -111,43 +72,15 @@ def _split_and_distribute_coins(
dlen = len(distro)
partial_balance = int(total_balance / (dlen if dlen > 1 else 2))
distro_balances = [partial_balance for x in distro]
# result = txn.split_coin(coin=txn.gas, amounts=distro_balances)
result = txn.split_coin(coin=txn.gas, amounts=distro_balances)
for index, target in enumerate(distro):
txn.transfer_sui(
from_coin=txn.gas, recipient=target, amount=distro_balances[index]
)
txn.transfer_objects(transfers=[result[index]], recipient=target)
# txn.transfer_sui(
# from_coin=txn.gas, recipient=target, amount=distro_balances[index]
# )
# txn.transfer_sui(recipient=target, from_coin=result[index])


def _inspect(
client: SuiGQLClient, txn: SuiTransaction, target_gas=pgql_type.SuiCoinObjectGQL
):
"""."""
print(txn.raw_kind().to_json(indent=2))
tx_bytes = txn.build(use_gas_objects=[target_gas])
result = client.execute_query_node(
with_node=qn.DryRunTransaction(tx_bytestr=tx_bytes)
)
if result.is_ok():
print(result.result_data.to_json(indent=2))
else:
raise ValueError(f"Error in dry run {result.result_string}")


def _execute(
client: SuiGQLClient, txn: SuiTransaction, target_gas=pgql_type.SuiCoinObjectGQL
):
"""."""
tx_b64, sig_array = txn.build_and_sign(use_gas_objects=[target_gas])
result = client.execute_query_node(
with_node=qn.ExecuteTransaction(tx_bytestr=tx_b64, sig_array=sig_array)
)
if result.is_ok():
print(result.result_data.to_json(indent=2))
else:
raise ValueError(f"Error in dry run {result.result_string}")


def main():
"""Main entry point."""
# Parse module meta data pulling out relevant content
Expand All @@ -156,7 +89,7 @@ def main():
cfg_file = False
# Handle a different client.yaml other than default
if arg_line and arg_line[0] == "--local":
cfg_file = True
print("suibase does not support Sui GraphQL at this time.")
arg_line = arg_line[1:]
parsed = splay_parser(arg_line)
print(parsed)
Expand All @@ -166,9 +99,9 @@ def main():
cfg = SuiConfig.default_config()

try:
_resolve_owner(cfg, parsed.owner, parsed.alias)
utils.util_resolve_owner(cfg, parsed.owner, parsed.alias)
sui_client = SuiGQLClient(config=cfg)
all_gas = _get_all_owner_gas(sui_client)
all_gas = utils.util_get_all_owner_gas(sui_client, cfg.active_address.address)
target_gas, transaction, total_balance = _consolidate_coin(
sui_client, all_gas, parsed.coins
)
Expand All @@ -182,9 +115,9 @@ def main():
use_addresses = [x.address for x in parsed.addresses]
_split_and_distribute_coins(transaction, use_addresses, total_balance)
if parsed.inspect:
_inspect(sui_client, transaction, target_gas)
utils.util_inspect(sui_client, transaction, target_gas)
else:
_execute(sui_client, transaction, target_gas)
utils.util_execute(sui_client, transaction, target_gas)

except ValueError as ve:
print(ve)
Expand Down
105 changes: 59 additions & 46 deletions pysui_gadgets/to_one/to_one.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,53 @@

# -*- coding: utf-8 -*-

"""pysui_gadgets: To One main module."""
"""pysui_gadgets: Collapse two or more coints for address."""


import sys
import argparse
from typing import Optional


from pysui import SyncClient, SuiConfig, handle_result
from pysui.sui.sui_txn import SyncTransaction
from pysui.sui.sui_utils import partition
from pysui import SuiConfig
from pysui.sui.sui_pgql.pgql_clients import SuiGQLClient
from pysui.sui.sui_pgql.pgql_sync_txn import SuiTransaction
import pysui_gadgets.utils.exec_helpers as utils
import pysui.sui.sui_pgql.pgql_types as pgql_type
from pysui_gadgets.utils.cmdlines import to_one_parser
from pysui_gadgets.utils.exec_helpers import add_owner_to_gas_object


def _join_coins(client: SyncClient, args: argparse.Namespace):
"""Using PayAllSui builder, join all mists from all gas object to one for an address."""
gas_res: list = handle_result(client.get_gas(args.address, True)).data
if len(gas_res) < 2:
print("Can't join with less than 2 coins")
return
# Resolve primary by argument or selection
if args.primary:
index = [x.object_id for x in gas_res].index(args.primary.value)
primary = gas_res.pop(index)
else:
primary = gas_res[0]
gas_res = gas_res[1:]
owner = args.address.address
gas_res = [add_owner_to_gas_object(owner, x) for x in gas_res]
converted = 0

if len(gas_res) <= args.merge_threshold:
txn = SyncTransaction(client=client, initial_sender=args.address)
_ = txn.merge_coins(merge_to=txn.gas, merge_from=gas_res)
result = txn.execute(use_gas_object=primary.object_id)


def _to_one_coin(
client: SuiGQLClient,
coins: list[pgql_type.SuiCoinObjectGQL],
primary_coin: Optional[str],
) -> tuple[pgql_type.SuiCoinObjectGQL, SuiTransaction]:
"""Setup consolidation of coins in play."""

target_gas: pgql_type.SuiCoinObjectGQL = None
# If primary coin, remove from coin list
if primary_coin:
p_pos: int = -1
for index, coin in enumerate(coins):
if primary_coin == coin.object_id:
p_pos = index
target_gas = coin
coins.remove(target_gas)
break
if p_pos < 0:
raise ValueError(
f"Primary coin {primary_coin} not found in owner's gas coins"
)
else:
# Partition the gas_res into _MAX_INPUTS chunks
for chunk in list(partition(gas_res, args.merge_threshold)):
chunk_count = len(chunk)
txn = SyncTransaction(client=client, initial_sender=args.address)
_ = txn.merge_coins(merge_to=txn.gas, merge_from=chunk)
result = txn.execute(use_gas_object=primary.object_id)
if result.is_ok():
converted += chunk_count
else:
print(f"Failure on coin in range {converted} -> {result.result_string}")
return
print(f"Succesfully merged {converted} coins to {primary.object_id}")
print(handle_result(client.get_object(primary.object_id)).to_json(indent=2))
target_gas = coins[0]
coins = coins[1:]

# Merge all remaining coins to gas
# preserving the target gas coint
txn = SuiTransaction(client=client)
txn.merge_coins(merge_to=txn.gas, merge_from=coins)
return (
target_gas,
txn,
)


def main():
Expand All @@ -70,16 +68,31 @@ def main():
cfg_file = False
# Handle a different client.yaml other than default
if arg_line and arg_line[0] == "--local":
cfg_file = True
print("suibase does not support Sui GraphQL at this time.")
arg_line = arg_line[1:]
parsed = to_one_parser(arg_line)
if cfg_file:
cfg = SuiConfig.sui_base_config()
else:
cfg = SuiConfig.default_config()

# Run the job
_join_coins(SyncClient(cfg), parsed)
try:
utils.util_resolve_owner(cfg, parsed.owner, parsed.alias)
sui_client = SuiGQLClient(config=cfg)
all_gas = utils.util_get_all_owner_gas(sui_client, cfg.active_address.address)
if len(all_gas) == 1:
raise ValueError("to-one requires the owner has more than 1 gas coin.")

target_gas, transaction = _to_one_coin(
sui_client, all_gas, (parsed.primary.value if parsed.primary else None)
)
if parsed.inspect:
utils.util_inspect(sui_client, transaction, target_gas)
else:
utils.util_execute(sui_client, transaction, target_gas)

except ValueError as ve:
print(ve)


if __name__ == "__main__":
Expand Down
29 changes: 19 additions & 10 deletions pysui_gadgets/utils/cmdlines.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,21 @@ def to_one_parser(in_args: list) -> argparse.Namespace:
usage="%(prog)s [--command_options]",
description="Merges all SUI Gas mists 'to one' SUI Gas object for an address",
)
parser.add_argument(
"-a",
"--address",
required=True,
help="The address whose SUI coin to converge to one",
addy_arg_group = parser.add_mutually_exclusive_group(required=True)
addy_arg_group.add_argument(
"-o",
"--owner",
required=False,
help="The owner of coins to splay. Mutually exclusive with '-a'",
action=ValidateAddress,
)
addy_arg_group.add_argument(
"-a",
"--alias",
required=False,
help="Alias of owner of coins to splay. Mutually exclusive with '-o'.",
action=ValidateAlias,
)
parser.add_argument(
"-p",
"--primary",
Expand All @@ -120,13 +128,14 @@ def to_one_parser(in_args: list) -> argparse.Namespace:
action=ValidateObjectID,
)
parser.add_argument(
"-m",
"--merge-threshold",
"-i",
"--inspect",
help="Display transaction BCS structure and performs a dry run",
required=False,
default=10,
help="Sets the number of coins to merge at a time. Defaults to 10.",
type=check_positive,
action="store_true",
dest="inspect",
)

return parser.parse_args(in_args if in_args else ["--help"])


Expand Down
Loading

0 comments on commit 5f74c9a

Please sign in to comment.