Agenda
現象
Metaplex SugarでStandard NFTを作成後、Metaplex JSで verifyCollection で、冒頭で作成したStandard NFTをコレクション指定すると、以下のエラーが発生。
ターミナル:
/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:366
? new ParsedProgramError(program, resolvedError)
^
ParsedProgramError [MetaplexError]: TokenMetadataProgram > Can't use this function on unsized collection
>> Source: Program > TokenMetadataProgram [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s]
>> Problem: The program [TokenMetadataProgram] at address [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s] raised an error of code [101] that translates to "Can't use this function on unsized collection".
>> Solution: Check the error message provided by the program.
Caused By: UnsizedCollection: Can't use this function on unsized collection
at RpcClient.parseProgramError (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:366:9)
at RpcClient.sendTransaction (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:128:18)
... 3 lines matching cause stack trace ...
at async Disposable.run (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/utils/Disposable.ts:33:14) {
key: 'metaplex.errors.program.parsed_program_error',
title: "TokenMetadataProgram > Can't use this function on unsized collection",
problem: `The program [TokenMetadataProgram] at address [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s] raised an error of code [101] that translates to "Can't use this function on unsized collection".`,
solution: 'Check the error message provided by the program.',
source: 'program',
sourceDetails: 'TokenMetadataProgram [metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s]',
cause: UnsizedCollection: Can't use this function on unsized collection
at Object.errorResolver (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/nftModule/plugin.ts:69:16)
at RpcClient.parseProgramError (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:363:35)
at RpcClient.sendTransaction (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:128:18)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at async RpcClient.sendAndConfirmTransaction (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/plugins/rpcModule/RpcClient.ts:164:23)
at async TransactionBuilder.sendAndConfirm (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/utils/TransactionBuilder.ts:197:22)
at async Disposable.run (/Users/user/Desktop/temp/test-metaplex-js-v0.17.x/node_modules/@metaplex-foundation/js/src/utils/Disposable.ts:33:14) {
code: 101
},
logs: undefined,
program: {
name: 'TokenMetadataProgram',
address: PublicKey { _bn: [BN] },
errorResolver: [Function: errorResolver]
}
}
Collection NFT:
Metaplex Docs - Sugar をもとに、Collectionは含まずにNFTをMint(この場合、Collection NFTではなく、Standard NFTがMintされる)
Metaplex JS:
@metaplex-foundation/js - verifyCollection
import { Connection, clusterApiUrl, Keypair, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { Metaplex, keypairIdentity, bundlrStorage, toBigNumber } from "@metaplex-foundation/js";
import { createVerifyCollectionInstruction, SetAndVerifyCollectionStruct, setAndVerifySizedCollectionItemInstructionDiscriminator, VerifyCollectionInstructionAccounts, VerifyCollectionStruct } from "@metaplex-foundation/mpl-token-metadata";
import * as fs from 'fs';
import sleep from 'sleep';
const main = async () => {
const connection = new Connection(clusterApiUrl("devnet"));
const secretKey = new Uint8Array(JSON.parse(fs.readFileSync('src/assets/id.json', 'utf8')));
const wallet = Keypair.fromSecretKey(secretKey);
// const wallet = Keypair.generate();
// Ref: bundlrStorage: https://github.com/metaplex-foundation/js#bundlrstorage
const metaplex = Metaplex.make(connection)
.use(keypairIdentity(wallet))
.use(bundlrStorage({
address: 'https://devnet.bundlr.network',
providerUrl: 'https://api.devnet.solana.com',
timeout: 60000,
}));
// .use(mockStorage()); // Use this instead of bundlrStorage if you need mock(dummy url).
// -----------------------------------------------------------------
// Regular(Normal) NFT
// -----------------------------------------------------------------
const { uri } = await metaplex
.nfts()
.uploadMetadata({
name: "Regular NFT Metadata",
description: "Regular description",
image: "https://placekitten.com/200/300",
symbol: "paper",
});
const collectionAddress = new PublicKey('GvkSVzXPaVzVxZd3u7jiqWr1goeq7AczJmwya1cX7suB');
const { nft } = await metaplex
.nfts()
.create({
uri: uri,
name: "Regular NFT",
symbol: "paper",
sellerFeeBasisPoints: 500, // Represents 5.00%.
// maxSupply: toBigNumber(1),
maxSupply: toBigNumber(0),
tokenOwner: wallet.publicKey,
updateAuthority: wallet,
creators: [],
collection: collectionAddress
});
await metaplex
.nfts()
.verifyCollection({
mintAddress: nft.address,
collectionMintAddress: collectionAddress
})
};
main();
原因
Collection NFT側に「Collection Details」という、Collection NFTに紐付くStandard NFTがいくつあるのか管理するデータがある。
おそらく、このデータが更新できない(コレクションとして拡張する必要がある?)ためと思われる。
(もし原因の詳細が気になる場合は、ソースを直接読んだほうが早い)
sugar collection set
や metaboss collections migrate
などのCLIを使った場合は、正常にコレクション紐付けできたが(おそらくCollection NFT側も変更しているため?)、Metaplex JSの verifyCollection だとエラーが出てしまった。
Metaplex JSの verifyCollection は、おそらく Collection NFTの Collection Details をカウントアップするだけの機能っぽい気がする。
対応
sugar collection set
や metaboss collections migrate
などのCLIで、Collection NFT側を「Collection」として拡張させたあとに、Metaplex JSの verifyCollection を利用すると成功する。
補足
本件は、あとから Standard NFT → Collection NFT に変更するケースになるが、できれば先にMetaplex Sugarで Collection NFT と Standard NFT をセットで作成しておいてから、NFT運用するのがよさそう。
参考
StackOverflow - Error with newly migrated Certified NFT Collection