Skip to content

Commit

Permalink
Add proof arg and getAssetWithProof test for update metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
danenbm committed Jan 12, 2024
1 parent 7c259aa commit 078df86
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 3 deletions.
20 changes: 19 additions & 1 deletion clients/js/src/generated/instructions/updateMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '@metaplex-foundation/umi/serializers';
import { findTreeConfigPda } from '../accounts';
import {
PickPartial,
ResolvedAccount,
ResolvedAccountsWithIndices,
expectPublicKey,
Expand Down Expand Up @@ -113,8 +114,14 @@ export function getUpdateMetadataInstructionDataSerializer(): Serializer<
>;
}

// Extra Args.
export type UpdateMetadataInstructionExtraArgs = { proof: Array<PublicKey> };

// Args.
export type UpdateMetadataInstructionArgs = UpdateMetadataInstructionDataArgs;
export type UpdateMetadataInstructionArgs = PickPartial<
UpdateMetadataInstructionDataArgs & UpdateMetadataInstructionExtraArgs,
'proof'
>;

// Instruction.
export function updateMetadata(
Expand Down Expand Up @@ -228,12 +235,23 @@ export function updateMetadata(
);
resolvedAccounts.systemProgram.isWritable = false;
}
if (!resolvedArgs.proof) {
resolvedArgs.proof = [];
}

// Accounts in order.
const orderedAccounts: ResolvedAccount[] = Object.values(
resolvedAccounts
).sort((a, b) => a.index - b.index);

// Remaining Accounts.
const remainingAccounts = resolvedArgs.proof.map((value, index) => ({
index,
value,
isWritable: false,
}));
orderedAccounts.push(...remainingAccounts);

// Keys and Signers.
const [keys, signers] = getAccountMetasAndSigners(
orderedAccounts,
Expand Down
92 changes: 90 additions & 2 deletions clients/js/test/updateMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
none,
some,
publicKey,
PublicKey,
} from '@metaplex-foundation/umi';
import test from 'ava';
import {
Expand All @@ -14,8 +15,17 @@ import {
updateMetadata,
mintV1,
UpdateArgs,
findLeafAssetIdPda,
getAssetWithProof,
getMerkleProof,
hashMetadataCreators,
hashMetadataData,
} from '../src';
import { createTree, createUmi } from './_setup';
import { mint, createTree, createUmi } from './_setup';
import {
DasApiAsset,
GetAssetProofRpcResponse,
} from '@metaplex-foundation/digital-asset-standard-api';

test('it update the metadata of a minted compressed NFT', async (t) => {
// Given an empty Bubblegum tree.
Expand Down Expand Up @@ -73,7 +83,7 @@ test('it update the metadata of a minted compressed NFT', async (t) => {
nonce: 0,
index: 0,
currentMetadata: metadata,
//proof: [],
proof: [],
updateArgs: update_args,
}).sendAndConfirm(umi);

Expand All @@ -91,3 +101,81 @@ test('it update the metadata of a minted compressed NFT', async (t) => {
merkleTreeAccount = await fetchMerkleTree(umi, merkleTree);
t.is(merkleTreeAccount.tree.rightMostPath.leaf, publicKey(updatedLeaf));
});

test('it can update metadata using the getAssetWithProof helper', async (t) => {
// Given we increase the timeout for this test.
t.timeout(20000);

// And given a tree with several minted NFTs so that the proof is required.
const umi = await createUmi();
const merkleTree = await createTree(umi, { maxDepth: 5, maxBufferSize: 8 });
const preMints = [
await mint(umi, { merkleTree, leafIndex: 0 }),
await mint(umi, { merkleTree, leafIndex: 1 }),
await mint(umi, { merkleTree, leafIndex: 2 }),
await mint(umi, { merkleTree, leafIndex: 3 }),
await mint(umi, { merkleTree, leafIndex: 4 }),
await mint(umi, { merkleTree, leafIndex: 5 }),
await mint(umi, { merkleTree, leafIndex: 6 }),
await mint(umi, { merkleTree, leafIndex: 7 }),
];

// And a 9th minted NFT that we will use for the test.
const { metadata, leaf, leafIndex } = await mint(umi, {
merkleTree,
leafIndex: 8,
});

// And given we mock the RPC client to return the following asset and proof.
const merkleTreeAccount = await fetchMerkleTree(umi, merkleTree);
const [assetId] = findLeafAssetIdPda(umi, { merkleTree, leafIndex });
const rpcAsset = {
ownership: { owner: umi.identity.publicKey },
compression: {
leaf_id: leafIndex,
data_hash: publicKey(hashMetadataData(metadata)),
creator_hash: publicKey(hashMetadataCreators(metadata.creators)),
},
} as DasApiAsset;
const rpcAssetProof = {
proof: getMerkleProof([...preMints.map((m) => m.leaf), leaf], 5, leaf),
root: publicKey(getCurrentRoot(merkleTreeAccount.tree)),
tree_id: merkleTree,
node_index: leafIndex + 2 ** 5,
} as GetAssetProofRpcResponse;
umi.rpc = {
...umi.rpc,
getAsset: async (givenAssetId: PublicKey) => {
t.is(givenAssetId, assetId);
return rpcAsset;
},
getAssetProof: async (givenAssetId: PublicKey) => {
t.is(givenAssetId, assetId);
return rpcAssetProof;
},
};

// When we use the getAssetWithProof helper.
const assetWithProof = await getAssetWithProof(umi, assetId);

// Then we can use it to update metadata for the NFT.
const update_args: UpdateArgs = {
name: some('New name'),
symbol: none(),
uri: some('https://updated-example.com/my-nft.json'),
creators: none(),
sellerFeeBasisPoints: none(),
primarySaleHappened: none(),
isMutable: none(),
};

await updateMetadata(umi, {
...assetWithProof,
currentMetadata: metadata,
updateArgs: update_args,
}).sendAndConfirm(umi);

// And the full asset and proof responses can be retrieved.
t.is(assetWithProof.rpcAsset, rpcAsset);
t.is(assetWithProof.rpcAssetProof, rpcAssetProof);
});
1 change: 1 addition & 0 deletions configs/kinobi.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ kinobi.update(
"verifyCreator",
"unverifyCreator",
"verifyLeaf",
"updateMetadata"
].includes(node.name),
transformer: (node) => {
k.assertInstructionNode(node);
Expand Down

0 comments on commit 078df86

Please sign in to comment.