Merge pull request #11740 from Chia-Network/nft1_extend_info

Extend API for NFT1
This commit is contained in:
William Allen
2022-06-02 21:15:42 -05:00
committed by GitHub
9 changed files with 214 additions and 80 deletions
+33 -8
View File
@@ -23,7 +23,7 @@ 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.config import load_config
from chia.util.ints import uint8, uint32, uint64
from chia.util.ints import uint8, uint32, uint64, uint16
from chia.util.keychain import KeyringIsLocked, bytes_to_mnemonic, generate_mnemonic
from chia.util.path import path_from_root
from chia.util.ws_message import WsRpcMessage, create_payload_dict
@@ -574,18 +574,20 @@ class WalletRpcApi:
pass
elif request["wallet_type"] == "nft_wallet":
for wallet in self.service.wallet_state_manager.wallets.values():
if wallet.type() == WalletType.NFT:
# TODO Modify this for NFT1
did_id: Optional[bytes32] = None
if "did_id" in request and request["did_id"] is not None:
did_id = bytes32.from_hexstr(request["did_id"])
if wallet.type() == WalletType.NFT and wallet.get_did() == did_id:
log.info("NFT wallet already existed, skipping.")
return {
"success": True,
"type": wallet.type(),
"wallet_id": wallet.id(),
}
async with self.service.wallet_state_manager.lock:
nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
wallet_state_manager,
main_wallet,
wallet_state_manager, main_wallet, did_id, request.get("name", None)
)
return {
"success": True,
@@ -1363,7 +1365,17 @@ class WalletRpcApi:
]
)
fee = uint64(request.get("fee", 0))
spend_bundle = await nft_wallet.generate_new_nft(metadata, royalty_puzhash, target_puzhash, fee=fee)
did_id = request.get("did_id", None)
if did_id is not None:
did_id = bytes.fromhex(did_id)
spend_bundle = await nft_wallet.generate_new_nft(
metadata,
royalty_puzhash,
target_puzhash,
uint16(request.get("royalty_percentage", 0)),
did_id,
fee,
)
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
async def nft_get_nfts(self, request) -> Dict:
@@ -1421,6 +1433,8 @@ class WalletRpcApi:
odd_coin += 1
if odd_coin > 1:
return {"success": False, "error": "This is not a singleton, multiple children coins found."}
if odd_coin == 0:
return {"success": False, "error": "Cannot find child coin, please wait then retry."}
coin_state = coin_state_list[0]
# Get parent coin
parent_coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
@@ -1448,8 +1462,8 @@ class WalletRpcApi:
break
except Exception:
log.info(f"Inner solution is not a metadata updater solution: {inner_solution}")
uncurried_nft: UncurriedNFT = UncurriedNFT.uncurry(full_puzzle)
if update_condition is not None:
uncurried_nft: UncurriedNFT = UncurriedNFT.uncurry(full_puzzle)
metadata: Program = uncurried_nft.metadata
metadata = nft_puzzles.update_metadata(metadata, update_condition)
# Note: This is not the actual unspent NFT full puzzle.
@@ -1461,7 +1475,18 @@ class WalletRpcApi:
uncurried_nft.metadata_updater_hash,
uncurried_nft.inner_puzzle,
)
nft_info: NFTInfo = nft_puzzles.get_nft_info_from_puzzle(NFTCoinInfo(coin_state.coin, None, full_puzzle))
# Get launcher coin
launcher_coin: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
[uncurried_nft.singleton_launcher_id], peer=peer
)
if launcher_coin is None or len(launcher_coin) < 1 or launcher_coin[0].spent_height is None:
return {
"success": False,
"error": f"Launcher coin record 0x{uncurried_nft.singleton_launcher_id.hex()} not found",
}
nft_info: NFTInfo = nft_puzzles.get_nft_info_from_puzzle(
NFTCoinInfo(coin_state.coin, None, full_puzzle, launcher_coin[0].spent_height)
)
except Exception as e:
return {"success": False, "error": f"The coin is not a NFT. {e}"}
else:
+7 -2
View File
@@ -575,10 +575,11 @@ class WalletRpcClient(RpcClient):
await self.fetch("cancel_offer", {"trade_id": trade_id.hex(), "secure": secure, "fee": fee})
# NFT wallet
async def create_new_nft_wallet(self, did_wallet_id):
async def create_new_nft_wallet(self, did_id, name=None):
request: Dict[str, Any] = {
"wallet_type": "nft_wallet",
"did_wallet_id": did_wallet_id,
"did_id": did_id,
"name": name,
}
response = await self.fetch("create_new_wallet", request)
return response
@@ -597,6 +598,8 @@ class WalletRpcClient(RpcClient):
series_total=1,
series_number=1,
fee=0,
royalty_percentage=0,
did_id=None,
):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
@@ -610,6 +613,8 @@ class WalletRpcClient(RpcClient):
"license_uris": license_uris,
"series_number": series_number,
"series_total": series_total,
"royalty_percentage": royalty_percentage,
"did_id": did_id,
"fee": fee,
}
response = await self.fetch("nft_mint_nft", request)
+12 -2
View File
@@ -4,7 +4,7 @@ from typing import List, Optional
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.ints import uint16, uint64
from chia.util.ints import uint16, uint32, uint64
from chia.util.streamable import Streamable, streamable
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzles.load_clvm import load_clvm
@@ -23,9 +23,12 @@ class NFTInfo(Streamable):
nft_coin_id: bytes32
"""Current NFT coin ID"""
did_owner: Optional[bytes32]
owner_did: Optional[bytes32]
"""Owner DID"""
owner_pubkey: Optional[bytes]
"""Pubkey of the NFT owner"""
royalty: Optional[uint16]
"""Percentage of the transaction fee paid to the author, e.g. 1000 = 1%"""
@@ -59,6 +62,12 @@ class NFTInfo(Streamable):
chain_info: str
"""Information saved on the chain in hex"""
mint_height: uint32
"""Block height of the NFT minting"""
supports_did: bool
"""If the inner puzzle supports DID"""
pending_transaction: bool = False
"""Indicate if the NFT is pending for a transaction"""
@@ -72,6 +81,7 @@ class NFTCoinInfo(Streamable):
coin: Coin
lineage_proof: Optional[LineageProof]
full_puzzle: Program
mint_height: uint32
pending_transaction: bool = False
+19 -5
View File
@@ -107,6 +107,7 @@ def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo) -> NFTInfo:
uncurried_nft.singleton_launcher_id,
nft_coin_info.coin.name(),
uncurried_nft.owner_did,
uncurried_nft.owner_pubkey,
uncurried_nft.trade_price_percentage,
data_uris,
uncurried_nft.data_hash.as_python(),
@@ -118,6 +119,8 @@ def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo) -> NFTInfo:
uint64(uncurried_nft.series_number.as_int()),
uncurried_nft.metadata_updater_hash.as_python(),
disassemble(uncurried_nft.metadata),
nft_coin_info.mint_height,
uncurried_nft.supports_did,
nft_coin_info.pending_transaction,
)
return nft_info
@@ -179,12 +182,18 @@ def update_metadata(metadata: Program, update_condition: Program) -> Program:
def create_ownership_layer_puzzle(
nft_id: bytes32,
did_id: bytes32,
did_id: bytes,
p2_puzzle: Program,
percentage: uint16,
royalty_puzzle_hash: Optional[bytes32] = None,
) -> Program:
log.debug(f"Creating ownership layer puzzle with {nft_id} {did_id} {percentage} {p2_puzzle}")
log.debug(
"Creating ownership layer puzzle with NFT_ID: %s DID_ID: %s Royalty_Percentage: %d P2_puzzle: %s",
nft_id.hex(),
did_id.hex(),
percentage,
p2_puzzle,
)
singleton_struct = Program.to((SINGLETON_MOD_HASH, (nft_id, LAUNCHER_PUZZLE_HASH)))
if not royalty_puzzle_hash:
royalty_puzzle_hash = p2_puzzle.get_tree_hash()
@@ -205,13 +214,18 @@ def create_ownership_layer_puzzle(
def create_ownership_layer_transfer_solution(
new_did: bytes32,
new_did: bytes,
new_did_inner_hash: bytes32,
trade_prices_list: List[List[int]],
new_pubkey: G1Element,
conditions: List[Any] = [],
) -> Program:
log.debug(f"Creating a transfer solution with: {new_did} {new_did_inner_hash} {trade_prices_list} {new_pubkey}")
log.debug(
"Creating a transfer solution with: DID:%s Inner_puzhash:%s trade_price:%s pubkey:%s",
new_did.hex(),
new_did_inner_hash.hex(),
str(trade_prices_list),
new_pubkey,
)
puzhash = STANDARD_PUZZLE_MOD.curry(new_pubkey).get_tree_hash()
condition_list = [
[
+37 -11
View File
@@ -136,6 +136,9 @@ class NFTWallet:
def id(self) -> uint32:
return self.wallet_info.id
def get_did(self) -> Optional[bytes32]:
return self.did_id
async def get_confirmed_balance(self, record_list=None) -> uint128:
"""The NFT wallet doesn't really have a balance."""
return uint128(0)
@@ -202,7 +205,7 @@ class NFTWallet:
self.log.info(
f"found the info for NFT coin {coin_name} {uncurried_nft.inner_puzzle} {uncurried_nft.singleton_struct}"
)
singleton_id = bytes32(uncurried_nft.singleton_launcher_id)
singleton_id = uncurried_nft.singleton_launcher_id
parent_inner_puzhash = uncurried_nft.nft_state_layer.get_tree_hash()
metadata, p2_puzzle_hash = get_metadata_and_phs(
uncurried_nft, puzzle, Program.from_bytes(bytes(coin_spend.solution))
@@ -247,9 +250,18 @@ class NFTWallet:
if new_coin.puzzle_hash == child_puzzle.get_tree_hash():
child_coin = new_coin
break
else:
raise ValueError(f"Couldn't regenerate puzzle reveal for NFT: {coin_spend}")
launcher_coin_states: List[CoinState] = await self.wallet_state_manager.wallet_node.get_coin_state(
[singleton_id]
)
assert (
launcher_coin_states is not None
and len(launcher_coin_states) == 1
and launcher_coin_states[0].spent_height is not None
)
mint_height: uint32 = launcher_coin_states[0].spent_height
self.log.info("Adding a new NFT to wallet: %s", child_coin)
# all is well, lets add NFT to our local db
parent_coin = None
coin_record = await self.wallet_state_manager.coin_store.get_coin_record(coin_name)
@@ -263,20 +275,24 @@ class NFTWallet:
parent_coin = coin_record.coin
if parent_coin is None:
raise ValueError("Error finding parent")
await self.add_coin(
child_coin,
child_puzzle,
LineageProof(parent_coin.parent_coin_info, parent_inner_puzhash, parent_coin.amount),
mint_height,
in_transaction=in_transaction,
)
async def add_coin(self, coin: Coin, puzzle: Program, lineage_proof: LineageProof, in_transaction: bool) -> None:
async def add_coin(
self, coin: Coin, puzzle: Program, lineage_proof: LineageProof, mint_height: uint32, in_transaction: bool
) -> None:
my_nft_coins = self.nft_wallet_info.my_nft_coins
for coin_info in my_nft_coins:
if coin_info.coin == coin:
my_nft_coins.remove(coin_info)
my_nft_coins.append(NFTCoinInfo(coin, lineage_proof, puzzle))
my_nft_coins.append(NFTCoinInfo(coin, lineage_proof, puzzle, mint_height))
new_nft_wallet_info = NFTWalletInfo(
my_nft_coins,
self.nft_wallet_info.did_id,
@@ -336,11 +352,15 @@ class NFTWallet:
target_puzzle_hash: Optional[bytes32] = None,
royalty_puzzle_hash: Optional[bytes32] = None,
percentage: uint16 = uint16(0),
did_id: Optional[bytes] = None,
fee: uint64 = uint64(0),
) -> Optional[SpendBundle]:
"""
This must be called under the wallet state manager lock
"""
if self.did_id is not None and did_id is None:
# For a DID enabled NFT wallet it cannot mint NFT0. Mint NFT1 instead.
did_id = self.did_id
amount = uint64(1)
coins = await self.standard_wallet.select_coins(amount)
if coins is None:
@@ -355,10 +375,10 @@ class NFTWallet:
p2_inner_puzzle = await self.standard_wallet.get_new_puzzle()
if not target_puzzle_hash:
target_puzzle_hash = p2_inner_puzzle.get_tree_hash()
if self.did_id:
self.log.debug("Creating NFT using DID: %s", self.did_id)
if did_id is not None:
self.log.debug("Creating NFT using DID: %s", did_id)
inner_puzzle = create_ownership_layer_puzzle(
launcher_coin.name(), self.did_id, p2_inner_puzzle, percentage, royalty_puzzle_hash=royalty_puzzle_hash
launcher_coin.name(), did_id, p2_inner_puzzle, percentage, royalty_puzzle_hash=royalty_puzzle_hash
)
self.log.debug("Got back ownership inner puzzle: %s", disassemble(inner_puzzle))
else:
@@ -405,7 +425,7 @@ class NFTWallet:
target_puzzle_hash = p2_inner_puzzle.get_tree_hash()
record: Optional[DerivationRecord] = None
# Create inner solution for eve spend
if self.did_id:
if did_id is not None:
record = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
p2_inner_puzzle.get_tree_hash()
)
@@ -416,7 +436,7 @@ class NFTWallet:
did_inner_hash, did_bundle = await self.get_did_approval_info(launcher_coin.name())
pubkey = record.pubkey
self.log.debug("Going to use this pubkey for NFT mint: %s", pubkey)
innersol = create_ownership_layer_transfer_solution(self.did_id, did_inner_hash, [], pubkey)
innersol = create_ownership_layer_transfer_solution(did_id, did_inner_hash, [], pubkey)
bundles_to_agg.append(did_bundle)
self.log.debug("Created an inner DID NFT solution: %s", disassemble(innersol))
@@ -621,7 +641,13 @@ class NFTWallet:
raise ValueError(f"NFT coin {coin_id} doesn't exist.")
my_nft_coins.append(
NFTCoinInfo(target_nft.coin, target_nft.lineage_proof, target_nft.full_puzzle, pending_transaction)
NFTCoinInfo(
target_nft.coin,
target_nft.lineage_proof,
target_nft.full_puzzle,
target_nft.mint_height,
pending_transaction,
)
)
new_nft_wallet_info = NFTWalletInfo(
my_nft_coins,
+13 -1
View File
@@ -64,10 +64,17 @@ class UncurriedNFT:
p2_puzzle: Program
"""p2 puzzle of the owner, either for ownership layer or standard"""
# ownership layer fields
owner_did: Optional[bytes32]
"""Owner's DID"""
supports_did: bool
"""If the inner puzzle support the DID"""
owner_pubkey: Optional[G1Element]
"""Owner's Pubkey in the P2 puzzle"""
nft_inner_puzzle_hash: Optional[bytes32]
"""Puzzle hash of the ownership layer inner puzzle """
@@ -141,7 +148,9 @@ class UncurriedNFT:
royalty_percentage = None
nft_inner_puzzle_mod = None
mod, ol_args = inner_puzzle.uncurry()
supports_did = False
if mod == NFT_OWNERSHIP_LAYER:
supports_did = True
log.debug("Parsing ownership layer")
_, current_did, transfer_program, p2_puzzle = ol_args.as_iter()
_, p2_args = p2_puzzle.uncurry()
@@ -150,6 +159,9 @@ class UncurriedNFT:
_, _, royalty_address, royalty_percentage, _, _ = transfer_program_args.as_iter()
royalty_percentage = uint16(royalty_percentage.as_int())
current_did = current_did.atom
if current_did == b"":
# For unassigned NFT, set owner DID to None
current_did = None
pubkey = pubkey_sexp.atom
else:
log.debug("Creating a standard NFT puzzle")
@@ -175,8 +187,8 @@ class UncurriedNFT:
series_number=series_number,
series_total=series_total,
inner_puzzle=inner_puzzle,
# TODO: Set/Remove following fields after NFT1 implemented
owner_did=current_did,
supports_did=supports_did,
owner_pubkey=pubkey,
transfer_program=transfer_program,
transfer_program_curry_params=transfer_program_args,
+17 -10
View File
@@ -40,6 +40,7 @@ from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.did_wallet.did_wallet_puzzles import DID_INNERPUZ_MOD, create_fullpuz, match_did_puzzle
from chia.wallet.key_val_store import KeyValStore
from chia.wallet.nft_wallet.nft_info import NFTWalletInfo
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT
from chia.wallet.outer_puzzles import AssetType, match_puzzle
@@ -716,21 +717,27 @@ class WalletStateManager:
wallet_id = None
wallet_type = None
self.log.debug("Handling NFT: %s", coin_spend)
did_id = uncurried_nft.owner_did
for wallet_info in await self.get_all_wallet_info_entries():
if wallet_info.type == WalletType.NFT:
self.log.debug(
"Checking NFT wallet %r and inner puzzle %s",
wallet_info.name,
uncurried_nft.inner_puzzle.get_tree_hash(),
)
wallet_id = wallet_info.id
wallet_type = WalletType.NFT
nft_wallet_info: NFTWalletInfo = NFTWalletInfo.from_json_dict(json.loads(wallet_info.data))
if nft_wallet_info.did_id == did_id:
self.log.debug(
"Checking NFT wallet %r and inner puzzle %s",
wallet_info.name,
uncurried_nft.inner_puzzle.get_tree_hash(),
)
wallet_id = wallet_info.id
wallet_type = WalletType.NFT
if wallet_id is None:
# TODO Modify this for NFT1
self.log.info("Cannot find a NFT wallet, creating a new one.")
self.log.info(
"Cannot find a NFT wallet for NFT_ID: %s DID: %s, creating a new one.",
uncurried_nft.singleton_launcher_id,
did_id,
)
nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
self, self.main_wallet, name="NFT Wallet", in_transaction=True
self, self.main_wallet, did_id=did_id, name="NFT Wallet", in_transaction=True
)
wallet_id = uint32(nft_wallet.wallet_id)
wallet_type = WalletType.NFT
+1
View File
@@ -513,6 +513,7 @@ class TestDIDWallet:
pubkey = (
await did_wallet_3.wallet_state_manager.get_unused_derivation_record(did_wallet_3.wallet_info.id)
).pubkey
await time_out_assert(15, did_wallet.get_confirmed_balance, 101)
attest_data = (await did_wallet.create_attestment(coin.name(), new_ph, pubkey))[1]
spend_bundle_list = await wallet_node.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
+75 -41
View File
@@ -15,7 +15,6 @@ from chia.types.peer_info import PeerInfo
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint16, uint32, uint64
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.nft_wallet import uncurry_nft
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.wallet_types import WalletType
@@ -342,7 +341,8 @@ async def test_nft_wallet_rpc_creation_and_list(two_wallet_nodes: Any, trusted:
assert len(coins) == 2
uris = []
for coin in coins:
uris.append(coin.to_json_dict()["data_uris"][0])
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()]
@@ -431,6 +431,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
assert coins_response.get("success")
coins = 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(
@@ -471,6 +472,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
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
@@ -510,6 +512,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
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
@@ -523,7 +526,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
@pytest.mark.parametrize(
"trusted",
[True],
[True, False],
)
@pytest.mark.asyncio
async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) -> None:
@@ -534,7 +537,7 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
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:
@@ -565,7 +568,7 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
for _ in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await asyncio.sleep(5)
did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
)
@@ -578,20 +581,25 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await time_out_assert(15, wallet_0.get_pending_change_balance, 0)
hex_did_id = did_wallet.get_my_DID()
did_id = bytes32.fromhex(hex_did_id)
nft_wallet_0 = await NFTWallet.create_new_nft_wallet(
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET DID 1", did_id=did_id
)
metadata = Program.to(
[
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
("h", bytes.fromhex("D4584AD463139FA8C0D9F68F4B59F185")),
]
)
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id))
assert isinstance(res, dict)
assert res.get("success")
nft_wallet_0_id = res["wallet_id"]
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 5999999999999)
await time_out_assert(10, wallet_0.get_confirmed_balance, 5999999999999)
sb = await nft_wallet_0.generate_new_nft(metadata)
assert sb
# 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"],
}
)
assert resp.get("success")
sb = resp["spend_bundle"]
# ensure hints are generated
assert compute_memos(sb)
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
@@ -599,35 +607,61 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await time_out_assert(10, len, 1, nft_wallet_0.nft_wallet_info.my_nft_coins)
metadata = Program.to(
[
("u", ["https://url1"]),
("h", "0xD4584AD463139FA8C0D9F68F4B59F181"),
]
)
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 7999999999999 - 1)
await time_out_assert(10, wallet_0.get_confirmed_balance, 7999999999999 - 1)
sb = await nft_wallet_0.generate_new_nft(metadata)
assert sb
# 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 compute_memos(sb)
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await time_out_assert(10, len, 2, nft_wallet_0.nft_wallet_info.my_nft_coins)
# check if we uncurry well
last_nft_coin = nft_wallet_0.nft_wallet_info.my_nft_coins[-1]
unft = uncurry_nft.UncurriedNFT.uncurry(last_nft_coin.full_puzzle)
assert unft.data_uris == ["https://url1"]
assert unft.data_hash.atom == b"0xD4584AD463139FA8C0D9F68F4B59F181"
assert unft.owner_did == nft_wallet_0.nft_wallet_info.did_id
assert unft.owner_pubkey is not None
first_nft_coin = nft_wallet_0.nft_wallet_info.my_nft_coins[0]
unft = uncurry_nft.UncurriedNFT.uncurry(first_nft_coin.full_puzzle)
assert unft.data_uris == ["https://www.chia.net/img/branding/chia-logo.svg"]
assert unft.data_hash.atom == bytes.fromhex("D4584AD463139FA8C0D9F68F4B59F185")
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 9999999999998 - 1)
await time_out_assert(10, wallet_0.get_confirmed_balance, 9999999999998 - 1)
# Check DID NFT
time_left = 5.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
if coins_response.get("nft_list"):
break
await asyncio.sleep(0.5)
time_left -= 0.5
assert coins_response["nft_list"], isinstance(coins_response, dict)
assert coins_response.get("success")
coins = 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
assert did_nft["owner_pubkey"] is not None
# Check unassigned NFT
await asyncio.sleep(5)
nft_wallets = await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
assert len(nft_wallets) == 2
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallets[1].id))
assert coins_response["nft_list"], isinstance(coins_response, dict)
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
assert non_did_nft["owner_pubkey"] is not None