Skip to content

Commit

Permalink
[CHIA-424] Port chia wallet coins to @tx_out_cmd (#18023)
Browse files Browse the repository at this point in the history
This brings another set of commands to the `@tx_out_cmd` decorator which
gives it the capability to optionally push a transaction and export the
transactions to a local file.
  • Loading branch information
Quexington committed May 16, 2024
2 parents 15dfd0e + d423e18 commit 8f43e6c
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 23 deletions.
3 changes: 2 additions & 1 deletion chia/_tests/cmds/cmd_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,9 @@ async def send_transaction_multi(
tx_config: TXConfig,
coins: Optional[List[Coin]] = None,
fee: uint64 = uint64(0),
push: bool = True,
) -> SendTransactionMultiResponse:
self.add_to_log("send_transaction_multi", (wallet_id, additions, tx_config, coins, fee))
self.add_to_log("send_transaction_multi", (wallet_id, additions, tx_config, coins, fee, push))
name = bytes32([2] * 32)
return SendTransactionMultiResponse(
[STD_UTX],
Expand Down
3 changes: 3 additions & 0 deletions chia/_tests/cmds/wallet/test_coins.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ async def select_coins(
Coin(get_bytes32(3), get_bytes32(4), uint64(1234560000)),
],
1000000000,
True,
),
(
1,
Expand All @@ -152,6 +153,7 @@ async def select_coins(
Coin(get_bytes32(5), get_bytes32(6), uint64(300000000000)),
],
1000000000,
True,
),
],
}
Expand Down Expand Up @@ -213,6 +215,7 @@ async def get_coin_records_by_names(
DEFAULT_TX_CONFIG,
[Coin(get_bytes32(1), get_bytes32(2), uint64(100000000000))],
1000000000,
True,
)
],
}
Expand Down
41 changes: 24 additions & 17 deletions chia/cmds/coin_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ async def async_combine(
target_coin_amount: Decimal,
target_coin_ids_str: Sequence[str],
largest_first: bool,
) -> None:
push: bool,
) -> List[TransactionRecord]:
async with get_wallet_client(wallet_rpc_port, fingerprint) as (wallet_client, fingerprint, config):
target_coin_ids: List[bytes32] = [bytes32.from_hexstr(coin_id) for coin_id in target_coin_ids_str]
final_fee = uint64(int(fee * units["chia"]))
Expand All @@ -135,10 +136,10 @@ async def async_combine(
mojo_per_unit = get_mojo_per_unit(wallet_type)
except LookupError:
print(f"Wallet id: {wallet_id} not found.")
return
return []
if not await wallet_client.get_synced():
print("Wallet not synced. Please wait.")
return
return []
is_xch: bool = wallet_type == WalletType.STANDARD_WALLET # this lets us know if we are directly combining Chia

tx_config = CMDTXConfigLoader(
Expand All @@ -165,10 +166,10 @@ async def async_combine(
conf_coins = [cr for cr in conf_coins if cr.name in target_coin_ids]
if len(conf_coins) == 0:
print("No coins to combine.")
return
return []
if len(conf_coins) == 1:
print("Only one coin found, you need at least two coins to combine.")
return
return []
if largest_first:
conf_coins.sort(key=lambda r: r.coin.amount, reverse=True)
else:
Expand All @@ -181,15 +182,18 @@ async def async_combine(
total_amount: uint128 = uint128(sum(coin.amount for coin in removals))
if is_xch and total_amount - final_fee <= 0:
print("Total amount is less than 0 after fee, exiting.")
return
return []
target_ph: bytes32 = decode_puzzle_hash(await wallet_client.get_next_address(wallet_id, False))
additions = [{"amount": (total_amount - final_fee) if is_xch else total_amount, "puzzle_hash": target_ph}]
transaction: TransactionRecord = (
await wallet_client.send_transaction_multi(wallet_id, additions, tx_config, removals, final_fee)
await wallet_client.send_transaction_multi(wallet_id, additions, tx_config, removals, final_fee, push=push)
).transaction
tx_id = transaction.name.hex()
print(f"Transaction sent: {tx_id}")
print(f"To get status, use command: chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}")
if push:
print(f"Transaction sent: {tx_id}")
print(f"To get status, use command: chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}")

return [transaction]


async def async_split(
Expand All @@ -202,22 +206,23 @@ async def async_split(
amount_per_coin: Decimal,
target_coin_id_str: str,
# TODO: [add TXConfig args]
) -> None:
push: bool,
) -> List[TransactionRecord]:
async with get_wallet_client(wallet_rpc_port, fingerprint) as (wallet_client, fingerprint, config):
final_fee = uint64(int(fee * units["chia"]))
target_coin_id: bytes32 = bytes32.from_hexstr(target_coin_id_str)
if number_of_coins > 500:
print(f"{number_of_coins} coins is greater then the maximum limit of 500 coins.")
return
return []
try:
wallet_type = await get_wallet_type(wallet_id=wallet_id, wallet_client=wallet_client)
mojo_per_unit = get_mojo_per_unit(wallet_type)
except LookupError:
print(f"Wallet id: {wallet_id} not found.")
return
return []
if not await wallet_client.get_synced():
print("Wallet not synced. Please wait.")
return
return []
is_xch: bool = wallet_type == WalletType.STANDARD_WALLET # this lets us know if we are directly spitting Chia
final_amount_per_coin = uint64(int(amount_per_coin * mojo_per_unit))
total_amount = final_amount_per_coin * number_of_coins
Expand All @@ -231,7 +236,7 @@ async def async_split(
f"is less than the total amount of the split: {total_amount / mojo_per_unit}, exiting."
)
print("Try using a smaller fee or amount.")
return
return []
additions: List[Dict[str, Union[uint64, bytes32]]] = []
for i in range(number_of_coins): # for readability.
# we always use new addresses
Expand All @@ -244,12 +249,13 @@ async def async_split(

transaction: TransactionRecord = (
await wallet_client.send_transaction_multi(
wallet_id, additions, tx_config, [removal_coin_record.coin], final_fee
wallet_id, additions, tx_config, [removal_coin_record.coin], final_fee, push=push
)
).transaction
tx_id = transaction.name.hex()
print(f"Transaction sent: {tx_id}")
print(f"To get status, use command: chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}")
if push:
print(f"Transaction sent: {tx_id}")
print(f"To get status, use command: chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}")
dust_threshold = config.get("xch_spam_amount", 1000000) # min amount per coin in mojo
spam_filter_after_n_txs = config.get("spam_filter_after_n_txs", 200) # how many txs to wait before filtering
if final_amount_per_coin < dust_threshold and wallet_type == WalletType.STANDARD_WALLET:
Expand All @@ -259,3 +265,4 @@ async def async_split(
f"{'will' if number_of_coins > spam_filter_after_n_txs else 'may'} not show up in your wallet unless "
f"you decrease the dust limit to below {final_amount_per_coin} mojos or disable it by setting it to 0."
)
return [transaction]
18 changes: 13 additions & 5 deletions chia/cmds/coins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import asyncio
from decimal import Decimal
from typing import Optional, Sequence
from typing import List, Optional, Sequence

import click

from chia.cmds import options
from chia.cmds.cmds_util import tx_out_cmd
from chia.wallet.transaction_record import TransactionRecord


@click.group("coins", help="Manage your wallets coins")
Expand Down Expand Up @@ -150,6 +152,7 @@ def list_cmd(
default=False,
help="Sort coins from largest to smallest or smallest to largest.",
)
@tx_out_cmd
def combine_cmd(
wallet_rpc_port: Optional[int],
fingerprint: int,
Expand All @@ -162,10 +165,11 @@ def combine_cmd(
fee: str,
input_coins: Sequence[str],
largest_first: bool,
) -> None:
push: bool,
) -> List[TransactionRecord]:
from .coin_funcs import async_combine

asyncio.run(
return asyncio.run(
async_combine(
wallet_rpc_port=wallet_rpc_port,
fingerprint=fingerprint,
Expand All @@ -178,6 +182,7 @@ def combine_cmd(
target_coin_amount=Decimal(target_amount),
target_coin_ids_str=input_coins,
largest_first=largest_first,
push=push,
)
)

Expand Down Expand Up @@ -216,6 +221,7 @@ def combine_cmd(
required=True,
)
@click.option("-t", "--target-coin-id", type=str, required=True, help="The coin id of the coin we are splitting.")
@tx_out_cmd
def split_cmd(
wallet_rpc_port: Optional[int],
fingerprint: int,
Expand All @@ -224,10 +230,11 @@ def split_cmd(
fee: str,
amount_per_coin: str,
target_coin_id: str,
) -> None:
push: bool,
) -> List[TransactionRecord]:
from .coin_funcs import async_split

asyncio.run(
return asyncio.run(
async_split(
wallet_rpc_port=wallet_rpc_port,
fingerprint=fingerprint,
Expand All @@ -236,5 +243,6 @@ def split_cmd(
number_of_coins=number_of_coins,
amount_per_coin=Decimal(amount_per_coin),
target_coin_id_str=target_coin_id,
push=push,
)
)

0 comments on commit 8f43e6c

Please sign in to comment.