From aedccaa1784cb1cf3596ebf4dcb15fac4ffc335b Mon Sep 17 00:00:00 2001 From: Jeff Cruikshank Date: Wed, 13 Jul 2022 11:55:28 -0700 Subject: [PATCH] Calculate NFT royalty amount --- chia/cmds/wallet_funcs.py | 63 ++++++++++++++++++++++++++- chia/wallet/nft_wallet/nft_puzzles.py | 1 + 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index 88dac2bd8a..18daad4840 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -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,13 +516,33 @@ 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) + + 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) + ) + if nft_royalty_percentage > 0: + print("NFT Royalty Amount:") + nft_royalty_asset_id, nft_royalty_amount = calculate_nft_royalty_amount( + offered, requested, nft_coin_id, nft_royalty_percentage + ) + nft_royalty_currency = ( + "XCH" + if nft_royalty_asset_id == "xch" + else (await cat_name_resolver(bytes32.fromhex(nft_royalty_asset_id)))[1] + ) + nft_royalty_divisor = units["chia"] if nft_royalty_asset_id == "xch" else units["cat"] + print( + f" {Decimal(nft_royalty_amount) / nft_royalty_divisor} {nft_royalty_currency} ({nft_royalty_amount} mojos)" + ) + print(f"Included Fees: {Decimal(offer.bundle.fees()) / units['chia']}") if not examine_only: @@ -929,3 +950,43 @@ 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]: + 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] + return royalty_asset_id, royalty_amount + + +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 diff --git a/chia/wallet/nft_wallet/nft_puzzles.py b/chia/wallet/nft_wallet/nft_puzzles.py index c25ac6e836..01b6b7985a 100644 --- a/chia/wallet/nft_wallet/nft_puzzles.py +++ b/chia/wallet/nft_wallet/nft_puzzles.py @@ -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")