Fix chia wallet coins cli bugs (#14388)

* fix fee bug

The fee should not be taken into account when dealing with CAT's or other non XCH coins.

* fix broken CAT removals

it was never implemented ...

* Update wallet_rpc_api.py
This commit is contained in:
Jack Nelson 2023-01-24 20:33:25 -05:00 committed by GitHub
parent 69c7172152
commit 99fbb056e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 8 deletions

View File

@ -13,6 +13,7 @@ from chia.types.coin_record import CoinRecord
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
from chia.util.ints import uint64, uint128
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.wallet_types import WalletType
async def async_list(args: Dict[str, Any], wallet_client: WalletRpcClient, fingerprint: int) -> None:
@ -125,13 +126,14 @@ async def async_combine(args: Dict[str, Any], wallet_client: WalletRpcClient, fi
if not await wallet_client.get_synced():
print("Wallet not synced. Please wait.")
return
is_xch: bool = wallet_type == WalletType.STANDARD_WALLET # this lets us know if we are directly combining Chia
final_max_amount = uint64(int(max_amount * mojo_per_unit)) if not target_coin_ids else uint64(0)
final_min_coin_amount: uint64 = uint64(int(min_coin_amount * mojo_per_unit))
final_excluded_amounts: List[uint64] = [uint64(int(Decimal(amount) * mojo_per_unit)) for amount in excluded_amounts]
final_target_coin_amount = uint64(int(target_coin_amount * mojo_per_unit))
if final_target_coin_amount != 0: # if we have a set target, just use standard coin selection.
removals: List[Coin] = await wallet_client.select_coins(
amount=final_target_coin_amount + final_fee,
amount=(final_target_coin_amount + final_fee) if is_xch else final_target_coin_amount,
wallet_id=wallet_id,
max_coin_amount=final_max_amount,
min_coin_amount=final_min_coin_amount,
@ -163,11 +165,11 @@ async def async_combine(args: Dict[str, Any], wallet_client: WalletRpcClient, fi
if input("Would you like to Continue? (y/n): ") != "y":
return
total_amount: uint128 = uint128(sum(coin.amount for coin in removals))
if total_amount - final_fee <= 0:
if is_xch and total_amount - final_fee <= 0:
print("Total amount is less than 0 after fee, exiting.")
return
target_ph: bytes32 = decode_puzzle_hash(await wallet_client.get_next_address(wallet_id, False))
additions = [{"amount": total_amount - final_fee, "puzzle_hash": target_ph}]
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, removals, final_fee
)
@ -195,8 +197,11 @@ async def async_split(args: Dict[str, Any], wallet_client: WalletRpcClient, fing
if not await wallet_client.get_synced():
print("Wallet not synced. Please wait.")
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) + final_fee
total_amount = final_amount_per_coin * number_of_coins
if is_xch:
total_amount += final_fee
# get full coin record from name, and validate information about it.
removal_coin_record: CoinRecord = (await wallet_client.get_coin_records_by_names([target_coin_id]))[0]
if removal_coin_record.coin.amount < total_amount:

View File

@ -1375,6 +1375,9 @@ class WalletRpcApi:
puzzle_hashes.append(decode_puzzle_hash(request["inner_address"]))
if "memos" in request:
memos.append([mem.encode("utf-8") for mem in request["memos"]])
coins: Optional[Set[Coin]] = None
if "coins" in request and len(request["coins"]) > 0:
coins = set([Coin.from_json_dict(coin_json) for coin_json in request["coins"]])
fee: uint64 = uint64(request.get("fee", 0))
min_coin_amount: uint64 = uint64(request.get("min_coin_amount", 0))
max_coin_amount: uint64 = uint64(request.get("max_coin_amount", 0))
@ -1385,7 +1388,7 @@ class WalletRpcApi:
exclude_coin_amounts = [uint64(a) for a in exclude_coin_amounts]
exclude_coin_ids: Optional[List] = request.get("exclude_coin_ids")
if exclude_coin_ids is not None:
exclude_coins: Set[Coin] = {
exclude_coins: Optional[Set[Coin]] = {
wr.coin
for wr in await self.service.wallet_state_manager.coin_store.get_coin_records(
[bytes32.from_hexstr(hex_id) for hex_id in exclude_coin_ids]
@ -1393,13 +1396,14 @@ class WalletRpcApi:
if wr is not None
}
else:
exclude_coins = set()
exclude_coins = None
if hold_lock:
async with self.service.wallet_state_manager.lock:
txs: List[TransactionRecord] = await wallet.generate_signed_transaction(
amounts,
puzzle_hashes,
fee,
coins=coins,
memos=memos if memos else None,
min_coin_amount=min_coin_amount,
max_coin_amount=max_coin_amount,
@ -1413,6 +1417,7 @@ class WalletRpcApi:
amounts,
puzzle_hashes,
fee,
coins=coins,
memos=memos if memos else None,
min_coin_amount=min_coin_amount,
max_coin_amount=max_coin_amount,

View File

@ -635,6 +635,7 @@ class WalletRpcClient(RpcClient):
exclude_amounts: Optional[List[uint64]] = None,
exclude_coin_ids: Optional[List[str]] = None,
additions: Optional[List[Dict[str, Any]]] = None,
removals: Optional[List[Coin]] = None,
) -> TransactionRecord:
send_dict: Dict[str, Any] = {
"wallet_id": wallet_id,
@ -657,6 +658,8 @@ class WalletRpcClient(RpcClient):
send_dict["additions"] = additions_hex
else:
raise ValueError("Must specify either amount and inner_address or additions")
if removals is not None and len(removals) > 0:
send_dict["coins"] = [c.to_json_dict() for c in removals]
res = await self.fetch("cat_spend", send_dict)
return TransactionRecord.from_json_dict_convenience(res["transaction"])

View File

@ -508,6 +508,7 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir
generated_funds = await generate_funds(full_node_api, env.wallet_1)
removals = await client.select_coins(1750000000000, wallet_id=1) # we want a coin that won't be selected by default
outputs = await create_tx_outputs(wallet_2, [(uint64(1), ["memo_1"]), (uint64(2), ["memo_2"])])
amount_outputs = sum(output["amount"] for output in outputs)
amount_fee = uint64(amount_outputs + 1)
@ -515,6 +516,7 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir
send_tx_res: TransactionRecord = await client.send_transaction_multi(
1,
outputs,
coins=removals,
fee=amount_fee,
)
spend_bundle = send_tx_res.spend_bundle
@ -522,6 +524,7 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir
assert send_tx_res is not None
assert_tx_amounts(send_tx_res, outputs, amount_fee=amount_fee, change_expected=True)
assert send_tx_res.removals == removals
await farm_transaction(full_node_api, wallet_node, spend_bundle)
@ -709,6 +712,16 @@ async def test_cat_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
assert uncurry_puzzle(spend_bundle.coin_spends[0].puzzle_reveal.to_program()).mod == CAT_MOD
await farm_transaction(full_node_api, wallet_node, spend_bundle)
# Test CAT spend with a fee and pre-specified removals / coins
removals = await client.select_coins(amount=uint64(2), wallet_id=cat_0_id)
tx_res = await client.cat_spend(cat_0_id, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"], removals=removals)
assert tx_res.wallet_id == cat_0_id
spend_bundle = tx_res.spend_bundle
assert spend_bundle is not None
assert removals[0] in tx_res.removals
assert uncurry_puzzle(spend_bundle.coin_spends[0].puzzle_reveal.to_program()).mod == CAT_MOD
await farm_transaction(full_node_api, wallet_node, spend_bundle)
# Test unacknowledged CAT
await wallet_node.wallet_state_manager.interested_store.add_unacknowledged_token(
asset_id, "Unknown", uint32(10000), bytes32(b"\00" * 32)
@ -716,8 +729,8 @@ async def test_cat_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
cats = await client.get_stray_cats()
assert len(cats) == 1
await time_out_assert(20, get_confirmed_balance, 15, client, cat_0_id)
await time_out_assert(20, get_confirmed_balance, 5, client_2, cat_1_id)
await time_out_assert(20, get_confirmed_balance, 14, client, cat_0_id)
await time_out_assert(20, get_confirmed_balance, 6, client_2, cat_1_id)
# Test CAT coin selection
selected_coins = await client.select_coins(amount=1, wallet_id=cat_0_id)