mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 08:05:33 +03:00
Improve handling of unknown pending balances (likely change from addi… (#10984)
* Improve handling of unknown pending balances (likely change from adding a maker fee). Minor improvement for fingerprint selection -- enter/return selects the logged-in fingerprint. * Minor output formatting improvements when showing offer summaries. Minor wallet key selection improvements. Added tests for print_offer_summary * Linter fixes * isort fix * Coroutine -> Awaitable * Removed problematic fee calculation from get_pending_amounts per feedback.
This commit is contained in:
parent
45e4c7c156
commit
bb57ccffa9
@ -4,7 +4,7 @@ import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any, Callable, List, Optional, Tuple, Dict
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
import aiohttp
|
||||
|
||||
@ -24,6 +24,8 @@ from chia.wallet.trading.trade_status import TradeStatus
|
||||
from chia.wallet.transaction_record import TransactionRecord
|
||||
from chia.wallet.util.wallet_types import WalletType
|
||||
|
||||
CATNameResolver = Callable[[bytes32], Awaitable[Optional[Tuple[Optional[uint32], str]]]]
|
||||
|
||||
|
||||
def print_transaction(tx: TransactionRecord, verbose: bool, name, address_prefix: str, mojo_per_unit: int) -> None:
|
||||
if verbose:
|
||||
@ -308,21 +310,37 @@ def timestamp_to_time(timestamp):
|
||||
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
async def print_offer_summary(wallet_client: WalletRpcClient, sum_dict: dict):
|
||||
async def print_offer_summary(cat_name_resolver: CATNameResolver, sum_dict: Dict[str, int], has_fee: bool = False):
|
||||
for asset_id, amount in sum_dict.items():
|
||||
if asset_id == "xch":
|
||||
wid: str = "1"
|
||||
name: str = "XCH"
|
||||
unit: int = units["chia"]
|
||||
else:
|
||||
result = await wallet_client.cat_asset_id_to_name(bytes32.from_hexstr(asset_id))
|
||||
wid = "Unknown"
|
||||
description: str = ""
|
||||
unit: int = units["chia"]
|
||||
wid: str = "1" if asset_id == "xch" else ""
|
||||
mojo_amount: int = int(Decimal(amount))
|
||||
name: str = "XCH"
|
||||
if asset_id != "xch":
|
||||
name = asset_id
|
||||
unit = units["cat"]
|
||||
if result is not None:
|
||||
wid = str(result[0])
|
||||
name = result[1]
|
||||
print(f" - {name} (Wallet ID: {wid}): {Decimal(int(amount)) / unit} ({int(Decimal(amount))} mojos)")
|
||||
if asset_id == "unknown":
|
||||
name = "Unknown"
|
||||
unit = units["mojo"]
|
||||
if has_fee:
|
||||
description = " [Typically represents change returned from the included fee]"
|
||||
else:
|
||||
unit = units["cat"]
|
||||
result = await cat_name_resolver(bytes32.from_hexstr(asset_id))
|
||||
if result is not None:
|
||||
wid = str(result[0])
|
||||
name = result[1]
|
||||
output: str = f" - {name}"
|
||||
mojo_str: str = f"{mojo_amount} {'mojo' if mojo_amount == 1 else 'mojos'}"
|
||||
if len(wid) > 0:
|
||||
output += f" (Wallet ID: {wid})"
|
||||
if unit == units["mojo"]:
|
||||
output += f": {mojo_str}"
|
||||
else:
|
||||
output += f": {mojo_amount / unit} ({mojo_str})"
|
||||
if len(description) > 0:
|
||||
output += f" {description}"
|
||||
print(output)
|
||||
|
||||
|
||||
async def print_trade_record(record, wallet_client: WalletRpcClient, summaries: bool = False) -> None:
|
||||
@ -337,13 +355,16 @@ async def print_trade_record(record, wallet_client: WalletRpcClient, summaries:
|
||||
print("Summary:")
|
||||
offer = Offer.from_bytes(record.offer)
|
||||
offered, requested = offer.summary()
|
||||
outbound_balances: Dict[str, int] = offer.get_pending_amounts()
|
||||
fees: Decimal = Decimal(offer.bundle.fees())
|
||||
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
||||
print(" OFFERED:")
|
||||
await print_offer_summary(wallet_client, offered)
|
||||
await print_offer_summary(cat_name_resolver, offered)
|
||||
print(" REQUESTED:")
|
||||
await print_offer_summary(wallet_client, requested)
|
||||
print("Pending Balances:")
|
||||
await print_offer_summary(wallet_client, offer.get_pending_amounts())
|
||||
print(f"Fees: {Decimal(offer.bundle.fees()) / units['chia']}")
|
||||
await print_offer_summary(cat_name_resolver, requested)
|
||||
print("Pending Outbound Balances:")
|
||||
await print_offer_summary(cat_name_resolver, outbound_balances, has_fee=(fees > 0))
|
||||
print(f"Included Fees: {fees / units['chia']}")
|
||||
print("---------------")
|
||||
|
||||
|
||||
@ -411,12 +432,13 @@ async def take_offer(args: dict, wallet_client: WalletRpcClient, fingerprint: in
|
||||
return
|
||||
|
||||
offered, requested = offer.summary()
|
||||
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
||||
print("Summary:")
|
||||
print(" OFFERED:")
|
||||
await print_offer_summary(wallet_client, offered)
|
||||
await print_offer_summary(cat_name_resolver, offered)
|
||||
print(" REQUESTED:")
|
||||
await print_offer_summary(wallet_client, requested)
|
||||
print(f"Fees: {Decimal(offer.bundle.fees()) / units['chia']}")
|
||||
await print_offer_summary(cat_name_resolver, requested)
|
||||
print(f"Included Fees: {Decimal(offer.bundle.fees()) / units['chia']}")
|
||||
|
||||
if not examine_only:
|
||||
confirmation = input("Would you like to take this offer? (y/n): ")
|
||||
@ -534,7 +556,7 @@ async def get_wallet(wallet_client: WalletRpcClient, fingerprint: int = None) ->
|
||||
current_sync_status = "Syncing"
|
||||
else:
|
||||
current_sync_status = "Not Synced"
|
||||
print("Choose wallet key:")
|
||||
print("Wallet keys:")
|
||||
for i, fp in enumerate(fingerprints):
|
||||
row: str = f"{i+1}) "
|
||||
row += "* " if fp == logged_in_fingerprint else spacing
|
||||
@ -543,15 +565,21 @@ async def get_wallet(wallet_client: WalletRpcClient, fingerprint: int = None) ->
|
||||
row += f" ({current_sync_status})"
|
||||
print(row)
|
||||
val = None
|
||||
prompt: str = (
|
||||
f"Choose a wallet key [1-{len(fingerprints)}] ('q' to quit, or Enter to use {logged_in_fingerprint}): "
|
||||
)
|
||||
while val is None:
|
||||
val = input("Enter a number to pick or q to quit: ")
|
||||
val = input(prompt)
|
||||
if val == "q":
|
||||
return None
|
||||
if not val.isdigit():
|
||||
elif val == "" and logged_in_fingerprint is not None:
|
||||
fingerprint = logged_in_fingerprint
|
||||
break
|
||||
elif not val.isdigit():
|
||||
val = None
|
||||
else:
|
||||
index = int(val) - 1
|
||||
if index >= len(fingerprints):
|
||||
if index < 0 or index >= len(fingerprints):
|
||||
print("Invalid value")
|
||||
val = None
|
||||
continue
|
||||
|
@ -193,12 +193,6 @@ class Offer:
|
||||
for addition in filter(lambda c: c.parent_coin_info == root_removal.name(), all_additions):
|
||||
pending_dict[name] += addition.amount
|
||||
|
||||
# Then we add a potential fee as pending XCH
|
||||
fee: int = sum(c.amount for c in all_removals) - sum(c.amount for c in all_additions)
|
||||
if fee > 0:
|
||||
pending_dict.setdefault("xch", 0)
|
||||
pending_dict["xch"] += fee
|
||||
|
||||
# Then we gather anything else as unknown
|
||||
sum_of_additions_so_far: int = sum(pending_dict.values())
|
||||
unknown: int = sum([c.amount for c in non_ephemeral_removals]) - sum_of_additions_so_far
|
||||
|
124
tests/core/cmds/test_wallet.py
Normal file
124
tests/core/cmds/test_wallet.py
Normal file
@ -0,0 +1,124 @@
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from chia.cmds.wallet_funcs import print_offer_summary
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.ints import uint32
|
||||
|
||||
TEST_DUCKSAUCE_ASSET_ID = "1000000000000000000000000000000000000000000000000000000000000001"
|
||||
TEST_CRUNCHBERRIES_ASSET_ID = "1000000000000000000000000000000000000000000000000000000000000002"
|
||||
TEST_UNICORNTEARS_ASSET_ID = "1000000000000000000000000000000000000000000000000000000000000003"
|
||||
|
||||
TEST_ASSET_ID_NAME_MAPPING: Dict[bytes32, Tuple[uint32, str]] = {
|
||||
bytes32.from_hexstr(TEST_DUCKSAUCE_ASSET_ID): (uint32(2), "DuckSauce"),
|
||||
bytes32.from_hexstr(TEST_CRUNCHBERRIES_ASSET_ID): (uint32(3), "CrunchBerries"),
|
||||
bytes32.from_hexstr(TEST_UNICORNTEARS_ASSET_ID): (uint32(4), "UnicornTears"),
|
||||
}
|
||||
|
||||
|
||||
async def cat_name_resolver(asset_id: bytes32) -> Optional[Tuple[Optional[uint32], str]]:
|
||||
return TEST_ASSET_ID_NAME_MAPPING.get(asset_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_xch(capsys: Any) -> None:
|
||||
summary_dict = {"xch": 1_000_000_000_000}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "XCH (Wallet ID: 1): 1.0 (1000000000000 mojos)" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_cat(capsys: Any) -> None:
|
||||
summary_dict = {
|
||||
TEST_DUCKSAUCE_ASSET_ID: 1_000,
|
||||
}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "DuckSauce (Wallet ID: 2): 1.0 (1000 mojos)" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_multiple_cats(capsys: Any) -> None:
|
||||
summary_dict = {
|
||||
TEST_DUCKSAUCE_ASSET_ID: 1_000,
|
||||
TEST_CRUNCHBERRIES_ASSET_ID: 2_000,
|
||||
}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "DuckSauce (Wallet ID: 2): 1.0 (1000 mojos)" in captured.out
|
||||
assert "CrunchBerries (Wallet ID: 3): 2.0 (2000 mojos)" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_xch_and_cats(capsys: Any) -> None:
|
||||
summary_dict = {
|
||||
"xch": 2_500_000_000_000,
|
||||
TEST_DUCKSAUCE_ASSET_ID: 1_111,
|
||||
TEST_CRUNCHBERRIES_ASSET_ID: 2_222,
|
||||
TEST_UNICORNTEARS_ASSET_ID: 3_333,
|
||||
}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "XCH (Wallet ID: 1): 2.5 (2500000000000 mojos)" in captured.out
|
||||
assert "DuckSauce (Wallet ID: 2): 1.111 (1111 mojos)" in captured.out
|
||||
assert "CrunchBerries (Wallet ID: 3): 2.222 (2222 mojos)" in captured.out
|
||||
assert "UnicornTears (Wallet ID: 4): 3.333 (3333 mojos)" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_xch_and_cats_with_zero_values(capsys: Any) -> None:
|
||||
summary_dict = {
|
||||
"xch": 0,
|
||||
TEST_DUCKSAUCE_ASSET_ID: 0,
|
||||
TEST_CRUNCHBERRIES_ASSET_ID: 0,
|
||||
TEST_UNICORNTEARS_ASSET_ID: 0,
|
||||
}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "XCH (Wallet ID: 1): 0.0 (0 mojos)" in captured.out
|
||||
assert "DuckSauce (Wallet ID: 2): 0.0 (0 mojos)" in captured.out
|
||||
assert "CrunchBerries (Wallet ID: 3): 0.0 (0 mojos)" in captured.out
|
||||
assert "UnicornTears (Wallet ID: 4): 0.0 (0 mojos)" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_cat_with_fee_and_change(capsys: Any) -> None:
|
||||
summary_dict = {
|
||||
TEST_DUCKSAUCE_ASSET_ID: 1_000,
|
||||
"unknown": 3_456,
|
||||
}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict, has_fee=True)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "DuckSauce (Wallet ID: 2): 1.0 (1000 mojos)" in captured.out
|
||||
assert "Unknown: 3456 mojos [Typically represents change returned from the included fee]" in captured.out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_print_offer_summary_xch_with_one_mojo(capsys: Any) -> None:
|
||||
summary_dict = {"xch": 1}
|
||||
|
||||
await print_offer_summary(cat_name_resolver, summary_dict)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert "XCH (Wallet ID: 1): 1e-12 (1 mojo)" in captured.out
|
Loading…
Reference in New Issue
Block a user