From 7a45a14eb68aac37426cefaea3c4412a24e36d86 Mon Sep 17 00:00:00 2001 From: Michael Danenberg <56533526+danenbm@users.noreply.github.com> Date: Fri, 12 Jan 2024 02:21:40 -0800 Subject: [PATCH] Add collection pass and fail test cases --- clients/js/test/updateMetadata.test.ts | 230 ++++++++++++++++++++++++- 1 file changed, 225 insertions(+), 5 deletions(-) diff --git a/clients/js/test/updateMetadata.test.ts b/clients/js/test/updateMetadata.test.ts index 0d992630..7551af6a 100644 --- a/clients/js/test/updateMetadata.test.ts +++ b/clients/js/test/updateMetadata.test.ts @@ -1,3 +1,7 @@ +import { + createNft, + findMetadataPda, +} from '@metaplex-foundation/mpl-token-metadata'; import { defaultPublicKey, generateSigner, @@ -5,7 +9,9 @@ import { some, publicKey, PublicKey, + percentAmount, } from '@metaplex-foundation/umi'; +import { generateSignerWithSol } from '@metaplex-foundation/umi-bundle-tests'; import test from 'ava'; import { MetadataArgsArgs, @@ -20,6 +26,7 @@ import { getMerkleProof, hashMetadataCreators, hashMetadataData, + mintToCollectionV1, } from '../src'; import { mint, createTree, createUmi } from './_setup'; import { @@ -27,7 +34,7 @@ import { GetAssetProofRpcResponse, } from '@metaplex-foundation/digital-asset-standard-api'; -test('it update the metadata of a minted compressed NFT', async (t) => { +test('it can update the metadata of a minted compressed NFT', async (t) => { // Given an empty Bubblegum tree. const umi = await createUmi(); const merkleTree = await createTree(umi); @@ -70,7 +77,6 @@ test('it update the metadata of a minted compressed NFT', async (t) => { name: some('New name'), uri: some('https://updated-example.com/my-nft.json'), }; - await updateMetadata(umi, { leafOwner, merkleTree, @@ -115,8 +121,10 @@ test('it can update metadata using the getAssetWithProof helper', async (t) => { await mint(umi, { merkleTree, leafIndex: 7 }), ]; - // And a 9th minted NFT that we will use for the test. + // And a 9th minted NFT owned by leafOwner. + const leafOwner = generateSigner(umi).publicKey; const { metadata, leaf, leafIndex } = await mint(umi, { + leafOwner, merkleTree, leafIndex: 8, }); @@ -125,7 +133,7 @@ test('it can update metadata using the getAssetWithProof helper', async (t) => { const merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); const [assetId] = findLeafAssetIdPda(umi, { merkleTree, leafIndex }); const rpcAsset = { - ownership: { owner: umi.identity.publicKey }, + ownership: { owner: leafOwner }, compression: { leaf_id: leafIndex, data_hash: publicKey(hashMetadataData(metadata)), @@ -158,9 +166,9 @@ test('it can update metadata using the getAssetWithProof helper', async (t) => { name: some('New name'), uri: some('https://updated-example.com/my-nft.json'), }; - await updateMetadata(umi, { ...assetWithProof, + leafOwner, currentMetadata: metadata, updateArgs, }).sendAndConfirm(umi); @@ -169,3 +177,215 @@ test('it can update metadata using the getAssetWithProof helper', async (t) => { t.is(assetWithProof.rpcAsset, rpcAsset); t.is(assetWithProof.rpcAssetProof, rpcAssetProof); }); + +test('it cannot update metadata using collection update authority when collection not verified', async (t) => { + // Given an empty Bubblegum tree. + const umi = await createUmi(); + const merkleTree = await createTree(umi); + const leafOwner = generateSigner(umi).publicKey; + let merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + + // And a Collection NFT. + const collectionMint = generateSigner(umi); + const collectionAuthority = generateSigner(umi); + await createNft(umi, { + mint: collectionMint, + authority: collectionAuthority, + name: 'My Collection', + uri: 'https://example.com/my-collection.json', + sellerFeeBasisPoints: percentAmount(5.5), // 5.5% + isCollection: true, + }).sendAndConfirm(umi); + + // When we mint a new NFT from the tree with an unverified collection. + const { metadata, leafIndex } = await mint(umi, { + leafOwner, + merkleTree, + metadata: { + collection: some({ key: collectionMint.publicKey, verified: false }), + }, + }); + + // Then we attempt to update metadata using the collection update authority. + const updateArgs: UpdateArgsArgs = { + name: some('New name'), + uri: some('https://updated-example.com/my-nft.json'), + }; + let promise = updateMetadata(umi, { + leafOwner, + merkleTree, + root: getCurrentRoot(merkleTreeAccount.tree), + nonce: leafIndex, + index: leafIndex, + currentMetadata: metadata, + proof: [], + updateArgs, + authority: collectionAuthority, + collectionMint: collectionMint.publicKey, + collectionMetadata: findMetadataPda(umi, { + mint: collectionMint.publicKey, + }), + }).sendAndConfirm(umi); + + // Then we expect a program error. + await t.throwsAsync(promise, { name: 'TreeAuthorityIncorrect' }); + + // And the leaf was not updated in the merkle tree. + const notUpdatedLeaf = hashLeaf(umi, { + merkleTree, + owner: leafOwner, + leafIndex, + metadata, + }); + merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + t.is(merkleTreeAccount.tree.rightMostPath.leaf, publicKey(notUpdatedLeaf)); +}); + +test('it can update metadata using collection update authority when collection is verified', async (t) => { + // Given an empty Bubblegum tree. + const umi = await createUmi(); + const merkleTree = await createTree(umi); + const leafOwner = generateSigner(umi).publicKey; + let merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + + // And a Collection NFT. + const collectionMint = generateSigner(umi); + const collectionAuthority = generateSigner(umi); + await createNft(umi, { + mint: collectionMint, + authority: collectionAuthority, + name: 'My Collection', + uri: 'https://example.com/my-collection.json', + sellerFeeBasisPoints: percentAmount(5.5), // 5.5% + isCollection: true, + }).sendAndConfirm(umi); + + // When we mint a new NFT from the tree using the following metadata, with collection verified. + const metadata: MetadataArgsArgs = { + name: 'My NFT', + uri: 'https://example.com/my-nft.json', + sellerFeeBasisPoints: 550, // 5.5% + collection: { + key: collectionMint.publicKey, + verified: true, + }, + creators: [], + }; + await mintToCollectionV1(umi, { + leafOwner, + merkleTree, + metadata, + collectionMint: collectionMint.publicKey, + collectionAuthority: collectionAuthority, + }).sendAndConfirm(umi); + + // And when metadata is updated. + const updateArgs: UpdateArgsArgs = { + name: some('New name'), + uri: some('https://updated-example.com/my-nft.json'), + }; + await updateMetadata(umi, { + leafOwner, + merkleTree, + root: getCurrentRoot(merkleTreeAccount.tree), + nonce: 0, + index: 0, + currentMetadata: metadata, + proof: [], + updateArgs, + authority: collectionAuthority, + collectionMint: collectionMint.publicKey, + collectionMetadata: findMetadataPda(umi, { + mint: collectionMint.publicKey, + }), + }).sendAndConfirm(umi); + + // Then the leaf was updated in the merkle tree. + const updatedLeaf = hashLeaf(umi, { + merkleTree, + owner: leafOwner, + leafIndex: 0, + metadata: { + ...metadata, + name: 'New name', + uri: 'https://updated-example.com/my-nft.json', + }, + }); + merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + t.is(merkleTreeAccount.tree.rightMostPath.leaf, publicKey(updatedLeaf)); +}); + +test('it cannot update metadata using tree owner when collection is verified', async (t) => { + // Given an empty Bubblegum tree. + const umi = await createUmi(); + const treeCreator = await generateSignerWithSol(umi); + const merkleTree = await createTree(umi, { treeCreator }); + const leafOwner = generateSigner(umi).publicKey; + let merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + + // And a Collection NFT. + const collectionMint = generateSigner(umi); + const collectionAuthority = generateSigner(umi); + await createNft(umi, { + mint: collectionMint, + authority: collectionAuthority, + name: 'My Collection', + uri: 'https://example.com/my-collection.json', + sellerFeeBasisPoints: percentAmount(5.5), // 5.5% + isCollection: true, + }).sendAndConfirm(umi); + + // When we mint a new NFT from the tree using the following metadata, with collection verified. + const metadata: MetadataArgsArgs = { + name: 'My NFT', + uri: 'https://example.com/my-nft.json', + sellerFeeBasisPoints: 550, // 5.5% + collection: { + key: collectionMint.publicKey, + verified: true, + }, + creators: [], + }; + await mintToCollectionV1(umi, { + leafOwner, + merkleTree, + metadata, + collectionMint: collectionMint.publicKey, + collectionAuthority: collectionAuthority, + treeCreatorOrDelegate: treeCreator, + }).sendAndConfirm(umi); + + // Then we attempt to update metadata using the tree owner. + const updateArgs: UpdateArgsArgs = { + name: some('New name'), + uri: some('https://updated-example.com/my-nft.json'), + }; + let promise = updateMetadata(umi, { + leafOwner, + merkleTree, + root: getCurrentRoot(merkleTreeAccount.tree), + nonce: 0, + index: 0, + currentMetadata: metadata, + proof: [], + updateArgs, + authority: treeCreator, + collectionMint: collectionMint.publicKey, + collectionMetadata: findMetadataPda(umi, { + mint: collectionMint.publicKey, + }), + }).sendAndConfirm(umi); + + // Then we expect a program error. + await t.throwsAsync(promise, { name: 'InvalidCollectionAuthority' }); + + // And the leaf was not updated in the merkle tree. + const notUpdatedLeaf = hashLeaf(umi, { + merkleTree, + owner: leafOwner, + leafIndex: 0, + metadata, + }); + merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); + t.is(merkleTreeAccount.tree.rightMostPath.leaf, publicKey(notUpdatedLeaf)); +});