Solana MetaplexでverifyCollectionすると「Can't use this function on unsized collection」エラー

現象

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 setmetaboss collections migrate などのCLIを使った場合は、正常にコレクション紐付けできたが(おそらくCollection NFT側も変更しているため?)、Metaplex JSの verifyCollection だとエラーが出てしまった。

Metaplex JSの verifyCollection は、おそらく Collection NFTの Collection Details をカウントアップするだけの機能っぽい気がする。

対応

sugar collection setmetaboss 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