from __future__ import annotations

import asyncio
import time
from typing import Any, Awaitable, Callable, Dict, List, Optional

import pytest
from chia_rs import AugSchemeMPL, G1Element, G2Element
from clvm_tools.binutils import disassemble

from chia._tests.conftest import ConsensusMode
from chia._tests.environments.wallet import WalletStateTransition, WalletTestFramework
from chia._tests.util.setup_nodes import OldSimulatorsAndWallets
from chia._tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
from chia.rpc.wallet_rpc_api import WalletRpcApi
from chia.simulator.full_node_simulator import FullNodeSimulator
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.peer_info import PeerInfo
from chia.types.signing_mode import CHIP_0002_SIGN_MESSAGE_PREFIX
from chia.types.spend_bundle import SpendBundle
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint32, uint64
from chia.util.timing import adjusted_timeout
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.nft_wallet.nft_info import NFTInfo
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.address_type import AddressType
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
from chia.wallet.util.wallet_types import WalletType
from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_state_manager import WalletStateManager


async def get_nft_count(wallet: NFTWallet) -> int:
    return await wallet.get_nft_count()


async def get_wallet_number(manager: WalletStateManager) -> int:
    return len(manager.wallets)


async def wait_rpc_state_condition(
    timeout: float,
    async_function: Callable[[Dict[str, Any]], Awaitable[Dict[str, Any]]],
    params: List[Dict[str, Any]],
    condition_func: Callable[[Dict[str, Any]], bool],
) -> Dict[str, Any]:
    __tracebackhide__ = True

    timeout = adjusted_timeout(timeout=timeout)

    start = time.monotonic()

    while True:
        resp = await async_function(*params)
        assert isinstance(resp, dict)
        if condition_func(resp):
            return resp

        now = time.monotonic()
        elapsed = now - start
        if elapsed >= timeout:
            raise asyncio.TimeoutError(
                f"timed out while waiting for {async_function.__name__}(): {elapsed} >= {timeout}",
            )

        await asyncio.sleep(0.3)


async def make_new_block_with(
    resp: Dict[str, Any], full_node_api: FullNodeSimulator, ph: bytes32, node_to_sync: Optional[WalletNode] = None
) -> SpendBundle:
    assert resp.get("success")
    sb = resp["spend_bundle"]
    assert isinstance(sb, SpendBundle)
    await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
    await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
    if node_to_sync is not None:
        await full_node_api.wait_for_wallet_synced(wallet_node=node_to_sync, timeout=30)
    return sb


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_wallet_creation_automatically(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet

    ph = await wallet_0.get_new_puzzlehash()
    ph1 = await wallet_1.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    nft_wallet_0 = await NFTWallet.create_new_nft_wallet(
        wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
    )
    metadata = Program.to(
        [("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
    )

    async with nft_wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_0.generate_new_nft(metadata, DEFAULT_TX_CONFIG, action_scope)

    await full_node_api.process_transaction_records(action_scope.side_effects.transactions)

    await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
    coins = await nft_wallet_0.get_current_nfts()
    assert len(coins) == 1, "nft not generated"

    async with nft_wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_0.generate_signed_transaction(
            [uint64(coins[0].coin.amount)], [ph1], DEFAULT_TX_CONFIG, action_scope, coins={coins[0].coin}
        )

    await full_node_api.process_transaction_records(action_scope.side_effects.transactions)

    async def num_wallets() -> int:
        return len(await wallet_node_1.wallet_state_manager.get_all_wallet_info_entries())

    await time_out_assert(30, num_wallets, 2)
    # Get the new NFT wallet
    nft_wallets = await wallet_node_1.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
    assert len(nft_wallets) == 1
    nft_wallet_1 = wallet_node_1.wallet_state_manager.wallets[nft_wallets[0].id]
    assert isinstance(nft_wallet_1, NFTWallet)
    await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
    await time_out_assert(30, get_nft_count, 1, nft_wallet_1)

    assert await nft_wallet_0.get_nft_count() == 0
    assert await nft_wallet_1.get_nft_count() == 1


@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant")
@pytest.mark.parametrize("wallet_environments", [{"num_environments": 2, "blocks_needed": [1, 1]}], indirect=True)
@pytest.mark.anyio
async def test_nft_wallet_creation_and_transfer(wallet_environments: WalletTestFramework) -> None:
    env_0 = wallet_environments.environments[0]
    env_1 = wallet_environments.environments[1]
    full_node_api = wallet_environments.full_node
    wallet_node_0 = env_0.node
    wallet_node_1 = env_1.node
    wallet_0 = env_0.xch_wallet
    wallet_1 = env_1.xch_wallet

    env_0.wallet_aliases = {
        "xch": 1,
        "nft": 2,
    }
    env_1.wallet_aliases = {
        "xch": 1,
        "nft": 2,
    }

    nft_wallet_0 = await NFTWallet.create_new_nft_wallet(
        wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
    )
    metadata = Program.to(
        [("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
    )
    async with nft_wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_0.generate_new_nft(metadata, DEFAULT_TX_CONFIG, action_scope)
    for tx in action_scope.side_effects.transactions:
        if tx.spend_bundle is not None:
            # ensure hints are generated
            assert len(compute_memos(tx.spend_bundle)) > 0

    await wallet_environments.process_pending_states(
        [
            WalletStateTransition(
                pre_block_balance_updates={
                    "xch": {
                        "confirmed_wallet_balance": 0,
                        "unconfirmed_wallet_balance": -1,
                        "<=#spendable_balance": -1,
                        "<=#max_send_amount": -1,
                        "pending_coin_removal_count": 1,
                        ">=#pending_change": 1,  # any amount increase
                        "unspent_coin_count": 0,
                    },
                    "nft": {
                        "init": True,
                        "confirmed_wallet_balance": 0,
                        "unconfirmed_wallet_balance": 0,
                        "spendable_balance": 0,
                        "max_send_amount": 0,
                        "pending_coin_removal_count": 1,
                        "pending_change": 0,
                        "unspent_coin_count": 0,
                    },
                },
                post_block_balance_updates={
                    "xch": {
                        "confirmed_wallet_balance": -1,
                        "unconfirmed_wallet_balance": 0,
                        ">=#spendable_balance": 1,  # any amount increase
                        ">=#max_send_amount": 1,  # any amount increase
                        "pending_coin_removal_count": -1,
                        "<=#pending_change": -1,  # any amount decrease
                        "unspent_coin_count": 0,
                    },
                    "nft": {
                        "pending_coin_removal_count": -1,
                        "unspent_coin_count": 1,
                    },
                },
            ),
            WalletStateTransition(),
        ]
    )

    await time_out_assert(10, get_nft_count, 1, nft_wallet_0)

    # Test Reorg mint
    height = full_node_api.full_node.blockchain.get_peak_height()
    assert height is not None
    await full_node_api.reorg_from_index_to_new_index(
        ReorgProtocol(uint32(height - 1), uint32(height + 1), bytes32([0] * 32), None)
    )
    await time_out_assert(60, full_node_api.full_node.blockchain.get_peak_height, height + 1)
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0)
    await env_0.change_balances(
        {
            "xch": {
                "set_remainder": True,  # Not testing XCH reorg functionality in this test
            },
            "nft": {
                # State back to before confirmation
                "unspent_coin_count": -1,
                "pending_coin_removal_count": 1,
            },
        }
    )
    await env_0.check_balances()

    await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
    await time_out_assert(30, get_wallet_number, 2, wallet_node_0.wallet_state_manager)

    new_metadata = Program.to([("u", ["https://www.test.net/logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F181")])

    async with nft_wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_0.generate_new_nft(new_metadata, DEFAULT_TX_CONFIG, action_scope)
    for tx in action_scope.side_effects.transactions:
        if tx.spend_bundle is not None:
            # ensure hints are generated
            assert len(compute_memos(tx.spend_bundle)) > 0

    await wallet_environments.process_pending_states(
        [
            WalletStateTransition(
                pre_block_balance_updates={
                    "xch": {
                        "confirmed_wallet_balance": 0,
                        "unconfirmed_wallet_balance": -1,
                        "<=#spendable_balance": -1,
                        "<=#max_send_amount": -1,
                        "pending_coin_removal_count": 1,
                        ">=#pending_change": 1,  # any amount increase
                        "unspent_coin_count": 0,
                    },
                    "nft": {
                        "pending_coin_removal_count": 1,
                    },
                },
                post_block_balance_updates={
                    "xch": {
                        "confirmed_wallet_balance": -2,
                        "unconfirmed_wallet_balance": 0,
                        ">=#spendable_balance": 1,  # any amount increase
                        ">=#max_send_amount": 1,  # any amount increase
                        "pending_coin_removal_count": -2,
                        "<=#pending_change": -1,  # any amount decrease
                        "unspent_coin_count": 0,
                    },
                    "nft": {
                        "pending_coin_removal_count": -2,
                        "unspent_coin_count": 2,
                    },
                },
            ),
            WalletStateTransition(),
        ]
    )

    await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
    coins = await nft_wallet_0.get_current_nfts()
    assert len(coins) == 2, "nft not generated"

    nft_wallet_1 = await NFTWallet.create_new_nft_wallet(
        wallet_node_1.wallet_state_manager, wallet_1, name="NFT WALLET 2"
    )
    async with nft_wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_0.generate_signed_transaction(
            [uint64(coins[1].coin.amount)],
            [await wallet_1.get_puzzle_hash(False)],
            DEFAULT_TX_CONFIG,
            action_scope,
            coins={coins[1].coin},
        )
    assert action_scope.side_effects.transactions[0].spend_bundle is not None

    assert len(compute_memos(action_scope.side_effects.transactions[0].spend_bundle)) > 0

    await wallet_environments.process_pending_states(
        [
            WalletStateTransition(
                pre_block_balance_updates={
                    "nft": {
                        "pending_coin_removal_count": 1,
                    }
                },
                post_block_balance_updates={
                    "nft": {
                        "pending_coin_removal_count": -1,
                        "unspent_coin_count": -1,
                    }
                },
            ),
            WalletStateTransition(
                pre_block_balance_updates={
                    "nft": {
                        "init": True,
                        "confirmed_wallet_balance": 0,
                        "unconfirmed_wallet_balance": 0,
                        "spendable_balance": 0,
                        "max_send_amount": 0,
                        "pending_coin_removal_count": 0,
                        "pending_change": 0,
                        "unspent_coin_count": 0,
                    }
                },
                post_block_balance_updates={
                    "nft": {
                        "unspent_coin_count": 1,
                    }
                },
            ),
        ]
    )

    await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
    await time_out_assert(30, get_nft_count, 1, nft_wallet_1)

    coins = await nft_wallet_1.get_current_nfts()
    assert len(coins) == 1

    # Send it back to original owner
    async with nft_wallet_1.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await nft_wallet_1.generate_signed_transaction(
            [uint64(coins[0].coin.amount)],
            [await wallet_0.get_puzzle_hash(False)],
            DEFAULT_TX_CONFIG,
            action_scope,
            coins={coins[0].coin},
        )
    assert action_scope.side_effects.transactions[0].spend_bundle is not None

    assert len(compute_memos(action_scope.side_effects.transactions[0].spend_bundle)) > 0

    await wallet_environments.process_pending_states(
        [
            WalletStateTransition(
                pre_block_balance_updates={},
                post_block_balance_updates={
                    "nft": {
                        "unspent_coin_count": 1,
                    }
                },
            ),
            WalletStateTransition(
                pre_block_balance_updates={
                    "nft": {
                        "pending_coin_removal_count": 1,
                    }
                },
                post_block_balance_updates={
                    "nft": {
                        "pending_coin_removal_count": -1,
                        "unspent_coin_count": -1,
                    }
                },
            ),
        ]
    )

    await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
    await time_out_assert(30, get_nft_count, 0, nft_wallet_1)

    # Test Reorg
    height = full_node_api.full_node.blockchain.get_peak_height()
    assert height is not None
    await full_node_api.reorg_from_index_to_new_index(
        ReorgProtocol(uint32(height - 1), uint32(height + 2), bytes32([0] * 32), None)
    )

    await full_node_api.wait_for_self_synced()
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, peak_height=uint32(height + 2))
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_1, peak_height=uint32(height + 2))

    await env_0.change_balances(
        {
            "nft": {
                "unspent_coin_count": -1,
            }
        }
    )
    await env_1.change_balances(
        {
            "nft": {
                "pending_coin_removal_count": 1,
                "unspent_coin_count": 1,
            }
        }
    )
    await env_0.check_balances()
    await env_1.check_balances()

    await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
    await time_out_assert(30, get_nft_count, 1, nft_wallet_1)


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_wallet_rpc_creation_and_list(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet

    ph = await wallet_0.get_new_puzzlehash()
    _ = await wallet_1.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    await time_out_assert(30, wallet_node_0.wallet_state_manager.synced, True)
    api_0 = WalletRpcApi(wallet_node_0)
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    nft_wallet_0 = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1"))
    assert isinstance(nft_wallet_0, dict)
    assert nft_wallet_0.get("success")
    nft_wallet_0_id = nft_wallet_0["wallet_id"]

    tr1 = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "artist_address": ph,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
        },
    )

    assert isinstance(tr1, dict)
    assert tr1.get("success")
    sb = tr1["spend_bundle"]

    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in tr1["transactions"]]
    await full_node_api.process_transaction_records(transactions)

    await wait_rpc_state_condition(30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"])
    tr2 = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "artist_address": ph,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F184",
            "uris": ["https://chialisp.com/img/logo.svg"],
            "meta_uris": [
                "https://bafybeigzcazxeu7epmm4vtkuadrvysv74lbzzbl2evphtae6k57yhgynp4.ipfs.nftstorage.link/6590.json"
            ],
            "meta_hash": "0x6a9cb99b7b9a987309e8dd4fd14a7ca2423858585da68cc9ec689669dd6dd6ab",
        },
    )
    assert isinstance(tr2, dict)
    assert tr2.get("success")
    sb = tr2["spend_bundle"]
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in tr2["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: x["success"] and len(x["nft_list"]) == 2
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    uris = []
    for coin in coins:
        assert not coin.supports_did
        uris.append(coin.data_uris[0])
        assert coin.mint_height > 0
    assert len(uris) == 2
    assert "https://chialisp.com/img/logo.svg" in uris
    assert bytes32.fromhex(coins[1].to_json_dict()["nft_coin_id"][2:]) in [x.name() for x in sb.additions()]

    coins_response = await wait_rpc_state_condition(
        5,
        api_0.nft_get_nfts,
        [{"wallet_id": nft_wallet_0_id, "start_index": 1, "num": 1}],
        lambda x: x["success"] and len(x["nft_list"]) == 1,
    )
    coins = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].data_hash.hex() == "0xD4584AD463139FA8C0D9F68F4B59F184"[2:].lower()

    # test counts

    resp = await wait_rpc_state_condition(
        10, api_0.nft_count_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: x["success"]
    )
    assert resp["count"] == 2
    resp = await wait_rpc_state_condition(10, api_0.nft_count_nfts, [{}], lambda x: x["success"])
    assert resp["count"] == 2
    resp = await wait_rpc_state_condition(
        10, api_0.nft_count_nfts, [{"wallet_id": 50}], lambda x: x["success"] is False
    )
    assert resp.get("count") is None


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_wallet_rpc_update_metadata(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet

    ph = await wallet_0.get_new_puzzlehash()
    _ = await wallet_1.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)

    api_0 = WalletRpcApi(wallet_node_0)
    await time_out_assert(30, wallet_node_0.wallet_state_manager.synced, True)
    await time_out_assert(30, wallet_node_1.wallet_state_manager.synced, True)
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    nft_wallet_0 = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1"))
    assert isinstance(nft_wallet_0, dict)
    assert nft_wallet_0.get("success")
    nft_wallet_0_id = nft_wallet_0["wallet_id"]

    # mint NFT
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "artist_address": ph,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
        },
    )

    assert resp.get("success")
    sb = resp["spend_bundle"]

    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    assert coins_response["nft_list"], isinstance(coins_response, dict)
    assert coins_response.get("success")
    coins: List[NFTInfo] = coins_response["nft_list"]
    coin = coins[0].to_json_dict()
    assert coin["mint_height"] > 0
    assert coin["data_hash"] == "0xd4584ad463139fa8c0d9f68f4b59f185"
    assert coin["chain_info"] == disassemble(
        Program.to(
            [
                ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
                ("h", hexstr_to_bytes("0xD4584AD463139FA8C0D9F68F4B59F185")),
                ("mu", []),
                ("lu", []),
                ("sn", uint64(1)),
                ("st", uint64(1)),
            ]
        )
    )
    # add another URI using a bech32m nft_coin_id
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    nft_coin_id = encode_puzzle_hash(
        bytes32.from_hexstr(coin["nft_coin_id"]), AddressType.NFT.hrp(api_0.service.config)
    )
    tr1 = await api_0.nft_add_uri(
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://metadata", "key": "mu"},
    )

    assert tr1.get("success")
    coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
    coins = coins_response["nft_list"]
    assert coins[0].pending_transaction
    sb = tr1["spend_bundle"]
    assert isinstance(sb, SpendBundle)
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in tr1["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    # check that new URI was added
    coins_response = await wait_rpc_state_condition(
        5,
        api_0.nft_get_nfts,
        [dict(wallet_id=nft_wallet_0_id)],
        lambda x: x["nft_list"] and len(x["nft_list"][0].metadata_uris) == 1,
    )
    coins = coins_response["nft_list"]
    assert len(coins) == 1
    coin = coins[0].to_json_dict()
    assert coin["mint_height"] > 0
    uris = coin["data_uris"]
    assert len(uris) == 1
    assert "https://www.chia.net/img/branding/chia-logo.svg" in uris
    assert len(coin["metadata_uris"]) == 1
    assert "http://metadata" == coin["metadata_uris"][0]
    assert len(coin["license_uris"]) == 0

    # add yet another URI, this time using a hex nft_coin_id
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    nft_coin_id = coin["nft_coin_id"]
    tr1 = await api_0.nft_add_uri(
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://data", "key": "u"}
    )

    assert isinstance(tr1, dict)
    assert tr1.get("success")
    sb = tr1["spend_bundle"]
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in tr1["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    coins_response = await wait_rpc_state_condition(
        5,
        api_0.nft_get_nfts,
        [dict(wallet_id=nft_wallet_0_id)],
        lambda x: x["nft_list"] and len(x["nft_list"][0].data_uris) == 2,
    )
    coins = coins_response["nft_list"]
    assert len(coins) == 1
    coin = coins[0].to_json_dict()
    assert coin["mint_height"] > 0
    uris = coin["data_uris"]
    assert len(uris) == 2
    assert len(coin["metadata_uris"]) == 1
    assert "http://data" == coin["data_uris"][0]


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_with_did_wallet_creation(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    # this shouldn't work
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert res["wallet_id"] == nft_wallet_0_id

    # now create NFT wallet with P2 standard puzzle for inner puzzle
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 0"))
    assert res["wallet_id"] != nft_wallet_0_id
    nft_wallet_p2_puzzle = res["wallet_id"]

    res = await api_0.nft_get_by_did({"did_id": hmr_did_id})
    assert nft_wallet_0_id == res["wallet_id"]
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999999)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999999)

    res = await api_0.nft_get_wallets_with_dids({})
    assert res.get("success")
    assert res.get("nft_wallets") == [
        {"wallet_id": nft_wallet_0_id, "did_id": hmr_did_id, "did_wallet_id": did_wallet.id()}
    ]

    res = await api_0.nft_get_wallet_did({"wallet_id": nft_wallet_0_id})
    assert res.get("success")
    assert res.get("did_id") == hmr_did_id

    # Create a NFT with DID
    nft_ph = await wallet_0.get_new_puzzlehash()
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "target_address": encode_puzzle_hash(nft_ph, "txch"),
        }
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]
    # ensure hints are generated correctly
    memos = compute_memos(sb)
    assert len(memos) > 0
    puzhashes = []
    for x in memos.values():
        puzhashes.extend(list(x))
    assert len(puzhashes) > 0
    matched = 0
    for puzhash in puzhashes:
        if puzhash.hex() == nft_ph.hex():
            matched += 1
    assert matched > 0

    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(transactions)

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999999 - 1)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999999 - 1)
    # Create a NFT without DID, this will go the unassigned NFT wallet
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "did_id": "",
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F181",
            "uris": ["https://url1"],
        }
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]

    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999998 - 1)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999998 - 1)
    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    did_nft = coins[0].to_json_dict()
    assert did_nft["mint_height"] > 0
    assert did_nft["supports_did"]
    assert did_nft["data_uris"][0] == "https://www.chia.net/img/branding/chia-logo.svg"
    assert did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F185".lower()
    assert did_nft["owner_did"][2:] == hex_did_id
    # Check unassigned NFT
    nft_wallets = await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
    assert len(nft_wallets) == 2
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_p2_puzzle}], lambda x: x["nft_list"]
    )
    assert coins_response["nft_list"]
    assert coins_response.get("success")
    coins = coins_response["nft_list"]
    assert len(coins) == 1
    non_did_nft = coins[0].to_json_dict()
    assert non_did_nft["mint_height"] > 0
    assert non_did_nft["supports_did"]
    assert non_did_nft["data_uris"][0] == "https://url1"
    assert non_did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F181".lower()
    assert non_did_nft["owner_did"] is None


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_rpc_mint(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()
    ph1 = await wallet_1.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    did_id = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=did_id))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999999)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999999)
    # Create a NFT with DID
    royalty_address = ph1
    data_hash_param = "0xD4584AD463139FA8C0D9F68F4B59F185"
    license_uris = ["http://mylicenseuri"]
    license_hash = "0xcafef00d"
    meta_uris = ["http://metauri"]
    meta_hash = "0xdeadbeef"
    royalty_percentage = 200
    sn = 10
    st = 100
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": data_hash_param,
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "license_uris": license_uris,
            "license_hash": license_hash,
            "meta_hash": meta_hash,
            "edition_number": sn,
            "edition_total": st,
            "meta_uris": meta_uris,
            "royalty_address": royalty_address,
            "target_address": ph,
            "royalty_percentage": royalty_percentage,
        }
    )
    assert resp.get("success")
    nft_id = str(resp.get("nft_id"))
    sb = resp["spend_bundle"]

    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999998)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999998)
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    did_nft = coins[0]
    assert did_nft.royalty_puzzle_hash == royalty_address
    assert did_nft.data_hash == bytes.fromhex(data_hash_param[2:])
    assert did_nft.metadata_hash == bytes.fromhex(meta_hash[2:])
    assert did_nft.metadata_uris == meta_uris
    assert did_nft.license_uris == license_uris
    assert did_nft.license_hash == bytes.fromhex(license_hash[2:])
    assert did_nft.edition_total == st
    assert did_nft.edition_number == sn
    assert did_nft.royalty_percentage == royalty_percentage
    assert decode_puzzle_hash(nft_id) == did_nft.launcher_id


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_transfer_nft_with_did(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    fee = 100
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    api_1 = WalletRpcApi(wallet_node_1)
    ph = await wallet_0.get_new_puzzlehash()
    ph1 = await wallet_1.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)
    )
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    # Create DID
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    # Create NFT wallet
    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    await time_out_assert(30, did_wallet.get_confirmed_balance, 1)
    await time_out_assert(30, did_wallet.get_unconfirmed_balance, 1)

    # Create a NFT with DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "fee": fee,
            "did_id": hmr_did_id,
        }
    )
    await make_new_block_with(resp, full_node_api, ph1)

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999898)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999898)
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is not None
    assert coins[0].owner_did.hex() == hex_did_id

    assert len(wallet_1.wallet_state_manager.wallets) == 1, "NFT wallet shouldn't exist yet"
    assert len(wallet_0.wallet_state_manager.wallets) == 3
    await full_node_api.wait_for_wallet_synced(wallet_node_0, 20)
    await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
    # transfer DID to the other wallet
    async with did_wallet.wallet_state_manager.new_action_scope(push=True) as action_scope:
        await did_wallet.transfer_did(ph1, uint64(0), True, DEFAULT_TX_CONFIG, action_scope)
    await full_node_api.process_transaction_records(action_scope.side_effects.transactions)
    await full_node_api.wait_for_wallet_synced(wallet_node_0, 20)
    await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
    await time_out_assert(15, len, 2, wallet_0.wallet_state_manager.wallets)
    # Transfer NFT, wallet will be deleted
    resp = await api_0.nft_transfer_nft(
        dict(
            wallet_id=nft_wallet_0_id,
            target_address=encode_puzzle_hash(ph1, "xch"),
            nft_coin_id=coins[0].nft_coin_id.hex(),
            fee=fee,
        )
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]
    assert len(compute_memos(sb)) > 0
    await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
    await make_new_block_with(resp, full_node_api, ph1)
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999798)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999798)
    await time_out_assert(30, len, 1, wallet_0.wallet_state_manager.wallets)

    # wait for all wallets to be created
    await time_out_assert(30, len, 3, wallet_1.wallet_state_manager.wallets)
    did_wallet_1 = wallet_1.wallet_state_manager.wallets[uint32(2)]
    assert nft_wallet_0_id not in wallet_node_0.wallet_state_manager.wallets.keys()
    # Check if the NFT owner DID is reset
    resp = await api_1.nft_get_by_did({})
    assert resp.get("success")
    nft_wallet_id_1 = resp.get("wallet_id")
    coins_response = await wait_rpc_state_condition(
        10, api_1.nft_get_nfts, [dict(wallet_id=nft_wallet_id_1)], lambda x: x["nft_list"]
    )
    assert len(coins_response["nft_list"]) == 1
    assert coins_response["nft_list"][0].owner_did is None
    assert coins_response["nft_list"][0].minter_did.hex() == hex_did_id
    nft_coin_id = coins_response["nft_list"][0].nft_coin_id

    await time_out_assert(30, did_wallet_1.get_spendable_balance, 1)

    # Set DID
    resp = await api_1.nft_set_nft_did(
        dict(wallet_id=nft_wallet_id_1, did_id=hmr_did_id, nft_coin_id=nft_coin_id.hex(), fee=fee)
    )
    txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(txs)

    coins_response = await wait_rpc_state_condition(
        5, api_1.nft_get_by_did, [dict(did_id=hmr_did_id)], lambda x: x.get("wallet_id", 0) > 0
    )
    await time_out_assert(30, wallet_1.get_unconfirmed_balance, 8000000000100)
    await time_out_assert(30, wallet_1.get_confirmed_balance, 8000000000100)
    nft_wallet_1_id = coins_response.get("wallet_id")
    assert nft_wallet_1_id
    # Check NFT DID is set now
    resp = await wait_rpc_state_condition(
        10,
        api_1.nft_get_nfts,
        [dict(wallet_id=nft_wallet_1_id)],
        lambda x: x["nft_list"] and x["nft_list"][0].owner_did,
    )
    coins = resp["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is not None
    assert coins[0].owner_did.hex() == hex_did_id


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_update_metadata_for_nft_did(
    self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    await time_out_assert(30, did_wallet.get_confirmed_balance, 1)

    # Create a NFT with DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did": hex_did_id,
        }
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]

    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    await full_node_api.process_transaction_records(transactions)

    # Check DID NFT

    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].minter_did is not None
    assert coins[0].minter_did.hex() == hex_did_id
    nft_coin_id = coins[0].nft_coin_id

    # add another URI
    tr1 = await api_0.nft_add_uri(
        {
            "wallet_id": nft_wallet_0_id,
            "nft_coin_id": nft_coin_id.hex(),
            "uri": "http://metadata",
            "key": "mu",
            "fee": 100,
        }
    )
    assert isinstance(tr1, dict)
    assert tr1.get("success")
    coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
    assert coins_response["nft_list"][0].pending_transaction

    sb = tr1["spend_bundle"]
    transactions = [TransactionRecord.from_json_dict_convenience(tx) for tx in tr1["transactions"]]
    await full_node_api.process_transaction_records(transactions)
    # check that new URI was added
    await time_out_assert(30, wallet_0.get_unconfirmed_balance, 3999999999898)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999898)
    coins_response = await wait_rpc_state_condition(
        5,
        api_0.nft_get_info,
        [dict(wallet_id=nft_wallet_0_id, coin_id=nft_coin_id.hex(), latest=True)],
        lambda x: x["nft_info"],
    )

    coin = coins_response["nft_info"].to_json_dict()
    assert coin["minter_did"][2:] == hex_did_id
    assert coin["mint_height"] > 0
    uris = coin["data_uris"]
    assert len(uris) == 1
    assert "https://www.chia.net/img/branding/chia-logo.svg" in uris
    assert len(coin["metadata_uris"]) == 1
    assert "http://metadata" == coin["metadata_uris"][0]
    assert len(coin["license_uris"]) == 0


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_bulk_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 2
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks + 1):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999999)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]
    assert isinstance(nft_wallet_0_id, uint32)
    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 2"))
    assert res.get("success")
    nft_wallet_1_id = res["wallet_id"]
    assert isinstance(nft_wallet_1_id, uint32)
    await time_out_assert(30, did_wallet.get_confirmed_balance, 1)

    # Create a NFT with DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": hmr_did_id,
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph, wallet_node_0)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
    )
    # Make a second one to test "bulk" updating in same wallet
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": hmr_did_id,
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph, wallet_node_0)
    await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
    )
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_1_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F186",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": "",
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph, wallet_node_0)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    nft1 = coins[0]
    nft12 = coins[1]
    assert len(coins) == 2
    assert coins[0].owner_did is not None
    assert coins[1].owner_did is not None
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_1_id}], lambda x: len(x["nft_list"]) == 1
    )
    coins = coins_response["nft_list"]
    nft2 = coins[0]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    nft_coin_list = [
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft1.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft12.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_1_id, "nft_coin_id": nft2.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_1_id},
        {"nft_coin_id": nft2.nft_coin_id.hex()},
    ]
    resp = await api_0.nft_set_did_bulk(dict(did_id=hmr_did_id, nft_coin_list=nft_coin_list, fee=1000))
    sb = resp["spend_bundle"]
    assert isinstance(sb, SpendBundle)
    assert len(sb.coin_spends) == 5
    tx_num = resp["tx_num"]
    assert isinstance(tx_num, int)
    assert tx_num == 5  # 1 for each NFT being spent (3), 1 for fee tx, 1 for did tx
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
    )
    coins = coins_response["nft_list"]
    assert coins[0].pending_transaction
    assert coins[1].pending_transaction
    await make_new_block_with(resp, full_node_api, ph, wallet_node_0)
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_by_did, [dict(did_id=hmr_did_id)], lambda x: x.get("wallet_id", 0) > 0
    )
    nft_wallet_1_id = coins_response.get("wallet_id")
    assert nft_wallet_1_id
    resp = await wait_rpc_state_condition(
        30,
        api_0.nft_get_nfts,
        [dict(wallet_id=nft_wallet_1_id)],
        lambda x: len(x["nft_list"]) > 2 and x["nft_list"][0].owner_did,
    )
    nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
    assert isinstance(nft_wallet_to_check, NFTWallet)
    assert await nft_wallet_to_check.get_nft_count() == 3
    coins = resp["nft_list"]
    assert len(coins) == 3
    assert coins[0].owner_did is not None
    assert coins[0].owner_did.hex() == hex_did_id
    assert coins[1].owner_did is not None
    assert coins[1].owner_did.hex() == hex_did_id
    assert coins[2].owner_did is not None
    assert coins[2].owner_did.hex() == hex_did_id


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_bulk_transfer(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 2
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    api_1 = WalletRpcApi(wallet_node_1)
    ph = await wallet_0.get_new_puzzlehash()
    ph1 = await wallet_1.get_new_puzzlehash()
    address = encode_puzzle_hash(ph1, AddressType.XCH.hrp(wallet_node_1.config))
    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks + 1):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    await full_node_api.wait_for_wallet_synced(wallet_node_0)

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
    await time_out_assert(30, wallet_0.get_confirmed_balance, 3999999999999)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]
    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 2"))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_1_id = res["wallet_id"]
    await time_out_assert(30, did_wallet.get_confirmed_balance, 1)

    # Create a NFT with DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": hmr_did_id,
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
    )
    # Make a second one to test "bulk" updating in same wallet
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": hmr_did_id,
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph)
    await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
    )
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_1_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F186",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": "",
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    nft1 = coins[0]
    nft12 = coins[1]
    assert len(coins) == 2
    assert coins[0].owner_did is not None
    assert coins[1].owner_did is not None
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_1_id}], lambda x: len(x["nft_list"]) == 1
    )
    coins = coins_response["nft_list"]
    nft2 = coins[0]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    nft_coin_list = [
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft1.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft12.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_1_id, "nft_coin_id": nft2.nft_coin_id.hex()},
        {"wallet_id": nft_wallet_1_id},
        {"nft_coin_id": nft2.nft_coin_id.hex()},
    ]
    resp = await api_0.nft_transfer_bulk(dict(target_address=address, nft_coin_list=nft_coin_list, fee=1000))
    assert len(resp["spend_bundle"].coin_spends) == 4
    assert resp["tx_num"] == 4
    sb = await make_new_block_with(resp, full_node_api, ph)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    await time_out_assert(30, get_wallet_number, 2, wallet_node_1.wallet_state_manager)
    coins_response = await wait_rpc_state_condition(
        30, api_1.nft_get_nfts, [{"wallet_id": 2}], lambda x: len(x["nft_list"]) == 3
    )
    coins = coins_response["nft_list"]
    nft_set = {nft1.launcher_id, nft12.launcher_id, nft2.launcher_id}
    assert coins[2].launcher_id in nft_set
    assert coins[1].launcher_id in nft_set
    assert coins[0].launcher_id in nft_set
    assert coins[0].owner_did is None
    assert coins[1].owner_did is None
    assert coins[2].owner_did is None


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 3
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    await full_node_api.wait_for_wallet_synced(wallet_node_0)

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    hex_did_id = did_wallet.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))

    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1"))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    await time_out_assert(30, did_wallet.get_confirmed_balance, 1)

    # Create a NFT without DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "did_id": "",
        }
    )
    sb = await make_new_block_with(resp, full_node_api, ph)
    # ensure hints are generated
    assert len(compute_memos(sb)) > 0

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
    )
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    nft_coin_id = coins[0].nft_coin_id

    # Test set None -> DID1
    async with wallet_0.wallet_state_manager.new_action_scope(push=True) as action_scope:
        did_wallet1 = await DIDWallet.create_new_did_wallet(
            wallet_node_0.wallet_state_manager, wallet_0, uint64(1), DEFAULT_TX_CONFIG, action_scope
        )
    await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
    await time_out_assert(30, did_wallet1.get_spendable_balance, 1)
    resp = await api_0.nft_set_nft_did(
        dict(wallet_id=nft_wallet_0_id, did_id=hmr_did_id, nft_coin_id=nft_coin_id.hex())
    )
    txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    txs = await did_wallet1.wallet_state_manager.add_pending_transactions(txs)
    await make_new_block_with(resp, full_node_api, ph)
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_by_did, [dict(did_id=hmr_did_id)], lambda x: x.get("wallet_id", 0) > 0
    )

    nft_wallet_1_id = coins_response.get("wallet_id")
    assert nft_wallet_1_id
    resp = await wait_rpc_state_condition(
        30,
        api_0.nft_get_nfts,
        [dict(wallet_id=nft_wallet_1_id)],
        lambda x: len(x["nft_list"]) > 0 and x["nft_list"][0].owner_did,
    )
    nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
    assert isinstance(nft_wallet_to_check, NFTWallet)
    assert len(await nft_wallet_to_check.get_current_nfts()) == 0

    coins = resp["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is not None
    assert coins[0].owner_did.hex() == hex_did_id
    nft_coin_id = coins[0].nft_coin_id

    resp = await api_0.nft_get_info(dict(coin_id=nft_coin_id.hex(), latest=True))
    assert resp["success"]
    assert coins[0] == resp["nft_info"]

    # Test set DID1 -> DID2
    hex_did_id = did_wallet1.get_my_DID()
    hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(wallet_node_0.config))
    resp = await api_0.nft_set_nft_did(
        dict(wallet_id=nft_wallet_1_id, did_id=hmr_did_id, nft_coin_id=nft_coin_id.hex())
    )
    txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    txs = await did_wallet1.wallet_state_manager.add_pending_transactions(txs)
    await make_new_block_with(resp, full_node_api, ph)
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_by_did, [dict(did_id=hmr_did_id)], lambda x: x.get("wallet_id") is not None
    )

    nft_wallet_2_id = coins_response.get("wallet_id")
    assert nft_wallet_2_id
    await time_out_assert(30, len, 6, wallet_node_0.wallet_state_manager.wallets)

    # Check NFT DID
    resp = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_2_id)], lambda x: len(x["nft_list"]) > 0
    )
    assert resp.get("success")
    coins = resp["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is not None
    assert coins[0].owner_did.hex() == hex_did_id
    nft_coin_id = coins[0].nft_coin_id
    resp = await api_0.nft_get_info(dict(coin_id=nft_coin_id.hex(), latest=True))
    assert resp["success"]
    assert coins[0] == resp["nft_info"]
    # Test set DID2 -> None
    resp = await api_0.nft_set_nft_did(dict(wallet_id=nft_wallet_2_id, nft_coin_id=nft_coin_id.hex()))
    txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in resp["transactions"]]
    txs = await did_wallet1.wallet_state_manager.add_pending_transactions(txs)
    await make_new_block_with(resp, full_node_api, ph)

    # Check NFT DID
    resp = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
    )
    coins = resp["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    assert nft_wallet_2_id in wallet_node_0.wallet_state_manager.wallets.keys()
    nft_coin_id = coins[0].nft_coin_id
    resp = await api_0.nft_get_info(dict(coin_id=nft_coin_id.hex(), latest=True))
    assert resp["success"]
    assert coins[0] == resp["nft_info"]


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_set_nft_status(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 5
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1"))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    # Create a NFT without DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
        },
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]

    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
    await make_new_block_with(resp, full_node_api, ph)

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: len(x["nft_list"]) > 0
    )
    assert coins_response["nft_list"], isinstance(coins_response, dict)
    assert coins_response.get("success")
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    assert not coins[0].pending_transaction
    nft_coin_id = coins[0].nft_coin_id
    # Set status
    resp = await api_0.nft_set_nft_status(
        dict(wallet_id=nft_wallet_0_id, coin_id=nft_coin_id.hex(), in_transaction=True)
    )
    assert resp.get("success")
    coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
    assert coins_response["nft_list"], isinstance(coins_response, dict)
    assert coins_response.get("success")
    coins = coins_response["nft_list"]
    assert coins[0].pending_transaction


@pytest.mark.parametrize("trusted", [True, False])
@pytest.mark.anyio
async def test_nft_sign_message(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
    num_blocks = 5
    full_nodes, wallets, _ = two_wallet_nodes
    full_node_api = full_nodes[0]
    full_node_server = full_node_api.server
    wallet_node_0, server_0 = wallets[0]
    wallet_node_1, server_1 = wallets[1]
    wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
    api_0 = WalletRpcApi(wallet_node_0)
    ph = await wallet_0.get_new_puzzlehash()

    if trusted:
        wallet_node_0.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
        wallet_node_1.config["trusted_peers"] = {
            full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
        }
    else:
        wallet_node_0.config["trusted_peers"] = {}
        wallet_node_1.config["trusted_peers"] = {}

    await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
    await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

    for _ in range(1, num_blocks):
        await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

    funds = sum(
        calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)
    )

    await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
    await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
    await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

    res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1"))
    assert isinstance(res, dict)
    assert res.get("success")
    nft_wallet_0_id = res["wallet_id"]

    # Create a NFT without DID
    resp = await api_0.nft_mint_nft(
        {
            "wallet_id": nft_wallet_0_id,
            "hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
            "uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
            "mu": ["https://www.chia.net/img/branding/chia-logo.svg"],
        },
    )
    assert resp.get("success")
    sb = resp["spend_bundle"]

    # ensure hints are generated
    assert len(compute_memos(sb)) > 0
    await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
    await make_new_block_with(resp, full_node_api, ph)

    # Check DID NFT
    coins_response = await wait_rpc_state_condition(
        30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: len(x["nft_list"]) > 0
    )
    assert coins_response["nft_list"], isinstance(coins_response, dict)
    assert coins_response.get("success")
    coins: List[NFTInfo] = coins_response["nft_list"]
    assert len(coins) == 1
    assert coins[0].owner_did is None
    assert not coins[0].pending_transaction
    # Test general string
    message = "Hello World"
    response = await api_0.sign_message_by_id(
        {"id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value), "message": message}
    )
    puzzle = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, message))
    assert AugSchemeMPL.verify(
        G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
        puzzle.get_tree_hash(),
        G2Element.from_bytes(bytes.fromhex(response["signature"])),
    )
    # Test hex string
    message = "0123456789ABCDEF"
    response = await api_0.sign_message_by_id(
        {"id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value), "message": message, "is_hex": True}
    )
    puzzle = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, bytes.fromhex(message)))
    assert AugSchemeMPL.verify(
        G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
        puzzle.get_tree_hash(),
        G2Element.from_bytes(bytes.fromhex(response["signature"])),
    )
    # Test BLS sign string
    message = "Hello World"
    response = await api_0.sign_message_by_id(
        {
            "id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value),
            "message": message,
            "is_hex": "False",
            "safe_mode": "False",
        }
    )

    assert AugSchemeMPL.verify(
        G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
        bytes(message, "utf-8"),
        G2Element.from_bytes(bytes.fromhex(response["signature"])),
    )
    # Test BLS sign hex
    message = "0123456789ABCDEF"
    response = await api_0.sign_message_by_id(
        {
            "id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value),
            "message": message,
            "is_hex": True,
            "safe_mode": False,
        }
    )

    assert AugSchemeMPL.verify(
        G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
        bytes.fromhex(message),
        G2Element.from_bytes(bytes.fromhex(response["signature"])),
    )
