Merge pull request #37 from Chia-Network/paninaro.cli_nft_offer_royalties

Update `chia wallet take_offer` to show NFT royalties
This commit is contained in:
William Allen
2022-07-14 11:56:16 -05:00
committed by GitHub
2 changed files with 78 additions and 1 deletions
+77 -1
View File
@@ -20,6 +20,7 @@ from chia.util.config import load_config
from chia.util.default_root import DEFAULT_ROOT_PATH
from chia.util.ints import uint16, uint32, uint64, uint128
from chia.wallet.did_wallet.did_info import DID_HRP
from chia.wallet.nft_wallet.nft_puzzles import NFT_METADATA_UPDATER_PUZZLE_HASH
from chia.wallet.nft_wallet.nft_info import NFT_HRP, NFTInfo
from chia.wallet.trade_record import TradeRecord
from chia.wallet.trading.offer import Offer
@@ -515,16 +516,50 @@ async def take_offer(args: dict, wallet_client: WalletRpcClient, fingerprint: in
print("Please enter a valid offer file or hex blob")
return
offered, requested, _ = offer.summary()
offered, requested, driver_dict = offer.summary()
cat_name_resolver = wallet_client.cat_asset_id_to_name
print("Summary:")
print(" OFFERED:")
await print_offer_summary(cat_name_resolver, offered)
print(" REQUESTED:")
await print_offer_summary(cat_name_resolver, requested)
print()
nft_coin_id: Optional[bytes32] = nft_coin_id_from_offer(driver_dict)
nft_royalty_percentage: int = (
0 if nft_coin_id is None else await get_nft_royalty_percentage(nft_coin_id, wallet_client)
)
nft_total_amount_requested_str: Optional[str] = None
if nft_royalty_percentage > 0:
print("NFT Royalty Fee:")
nft_royalty_asset_id, nft_royalty_amount, nft_total_amount_requested = calculate_nft_royalty_amount(
offered, requested, nft_coin_id, nft_royalty_percentage
)
nft_royalty_currency: str = "Unknown CAT"
if nft_royalty_asset_id == "xch":
nft_royalty_currency = "XCH"
else:
result = await cat_name_resolver(bytes32.fromhex(nft_royalty_asset_id))
if result is not None:
nft_royalty_currency = result[1]
nft_royalty_divisor = units["chia"] if nft_royalty_asset_id == "xch" else units["cat"]
nft_total_amount_requested_str = (
f"{Decimal(nft_total_amount_requested) / nft_royalty_divisor} {nft_royalty_currency}"
)
print(
f" {Decimal(nft_royalty_amount) / nft_royalty_divisor} {nft_royalty_currency} "
f"({nft_royalty_amount} mojos)"
)
print(f"Included Fees: {Decimal(offer.bundle.fees()) / units['chia']}")
if nft_total_amount_requested_str is not None:
print(f"Total Amount Requested: {nft_total_amount_requested_str}")
if not examine_only:
print()
confirmation = input("Would you like to take this offer? (y/n): ")
if confirmation in ["y", "yes"]:
trade_record = await wallet_client.take_offer(offer, fee=fee)
@@ -929,3 +964,44 @@ async def get_nft_info(args: Dict, wallet_client: WalletRpcClient, fingerprint:
print_nft_info(nft_info)
except Exception as e:
print(f"Failed to get NFT info: {e}")
async def get_nft_royalty_percentage(nft_coin_id: bytes32, wallet_client: WalletRpcClient) -> int:
info = NFTInfo.from_json_dict((await wallet_client.get_nft_info(nft_coin_id.hex()))["nft_info"])
return info.royalty_percentage
def calculate_nft_royalty_amount(
offered: Dict[str, Any], requested: Dict[str, Any], nft_coin_id: bytes32, nft_royalty_percentage: int
) -> Tuple[str, int, int]:
nft_asset_id = nft_coin_id.hex()
amount_dict: Dict[str, Any] = requested if nft_asset_id in offered else offered
amounts: List[Tuple[str, int]] = list(amount_dict.items())
if len(amounts) != 1 or not isinstance(amounts[0][1], int):
raise ValueError("Royalty enabled NFTs only support offering/requesting one NFT for one currency")
royalty_amount: uint64 = uint64(amounts[0][1] * nft_royalty_percentage / 10000)
royalty_asset_id = amounts[0][0]
total_amount_requested = (requested[royalty_asset_id] if amount_dict == requested else 0) + royalty_amount
return royalty_asset_id, royalty_amount, total_amount_requested
def driver_dict_asset_is_nft(driver_dict: Dict[str, Any], asset_id: str) -> bool:
asset_dict: Dict[str, Any] = driver_dict[asset_id]
if asset_dict.get("type") == "singleton":
updater_hash_hexstr: Optional[str] = asset_dict.get("also", {}).get("updater_hash")
try:
updater_hash = bytes32.from_hexstr(updater_hash_hexstr)
return updater_hash == NFT_METADATA_UPDATER_PUZZLE_HASH
except ValueError:
# Failed to construct bytes32 from updater_hash_hexstr
pass
return False
def nft_coin_id_from_offer(driver_dict: Dict[str, Any]) -> Optional[bytes32]:
nft_asset_id: Optional[str] = next(
(key for key in driver_dict.keys() if driver_dict_asset_is_nft(driver_dict, key)), None
)
return bytes32.fromhex(nft_asset_id) if nft_asset_id is not None else None
+1
View File
@@ -20,6 +20,7 @@ LAUNCHER_PUZZLE_HASH = LAUNCHER_PUZZLE.get_tree_hash()
SINGLETON_MOD_HASH = SINGLETON_TOP_LAYER_MOD.get_tree_hash()
NFT_STATE_LAYER_MOD_HASH = NFT_STATE_LAYER_MOD.get_tree_hash()
NFT_METADATA_UPDATER = load_clvm("nft_metadata_updater_default.clvm")
NFT_METADATA_UPDATER_PUZZLE_HASH = NFT_METADATA_UPDATER.get_tree_hash()
NFT_OWNERSHIP_LAYER = load_clvm("nft_ownership_layer.clvm")
NFT_TRANSFER_PROGRAM_DEFAULT = load_clvm("nft_ownership_transfer_program_one_way_claim_with_royalties.clvm")
STANDARD_PUZZLE_MOD = load_clvm("p2_delegated_puzzle_or_hidden_puzzle.clvm")