Metaplex Candy Machineで「PublicKeyMismatch: Public key mismatch」エラー

Metaplex Candy Machine v6.0.0.-alphaで、GuardsのsolPaymentを試していたときに発生したエラー。
長文になってしまうが、全文があったほうが理解しやすいため、一旦全部貼る。

現象

実行すると以下のエラー。

% ts-node mint.ts
PublicKeyMismatch: Public key mismatch

Source: Program > mplCandyGuard [Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g]

Caused By: Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 1: custom program error: 0x1772

Program Logs:
| Program ComputeBudget111111111111111111111111111111 invoke [1]
| Program ComputeBudget111111111111111111111111111111 success
| Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g invoke [1]
| Program log: Instruction: MintV2
| Program log: AnchorError thrown in program/src/utils.rs:95. Error Code: PublicKeyMismatch. Error Number: 6002. Error Message: Public key mismatch.
| Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g consumed 27348 of 800000 compute units
| Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g failed: custom program error: 0x1772

    at getMplCandyGuardErrorFromCode (/mint-site/node_modules/@metaplex-foundation/mpl-candy-machine/src/generated/errors/mplCandyGuard.ts:744:24)
    at Object.getErrorFromCode (/mint-site/node_modules/@metaplex-foundation/mpl-candy-machine/src/generated/programs/mplCandyGuard.ts:30:43)
    at Object.resolveError (/mint-site/node_modules/@metaplex-foundation/umi-program-repository/src/createDefaultProgramRepository.ts:122:35)
    at Object.sendTransaction (/mint-site/node_modules/@metaplex-foundation/umi-rpc-web3js/src/createWeb3JsRpc.ts:310:42)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async TransactionBuilder.sendAndConfirm (/mint-site/node_modules/@metaplex-foundation/umi/src/TransactionBuilder.ts:303:23)
    at async main (/mint-site/mint.ts:117:3) {
  source: 'program',
  sourceDetails: 'mplCandyGuard [Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g]',
  cause: SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 1: custom program error: 0x1772
      at Connection.sendEncodedTransaction (/mint-site/node_modules/@solana/web3.js/src/connection.ts:5860:13)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async Connection.sendRawTransaction (/mint-site/node_modules/@solana/web3.js/src/connection.ts:5819:20)
      at async Object.sendTransaction (/mint-site/node_modules/@metaplex-foundation/umi-rpc-web3js/src/createWeb3JsRpc.ts:302:25)
      at async TransactionBuilder.sendAndConfirm (/mint-site/node_modules/@metaplex-foundation/umi/src/TransactionBuilder.ts:303:23)
      at async main (/mint-site/mint.ts:117:3) {
    logs: [
      'Program ComputeBudget111111111111111111111111111111 invoke [1]',
      'Program ComputeBudget111111111111111111111111111111 success',
      'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g invoke [1]',
      'Program log: Instruction: MintV2',
      'Program log: AnchorError thrown in program/src/utils.rs:95. Error Code: PublicKeyMismatch. Error Number: 6002. Error Message: Public key mismatch.',
      'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g consumed 27348 of 800000 compute units',
      'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g failed: custom program error: 0x1772'
    ]
  },
  program: {
    name: 'mplCandyGuard',
    publicKey: { bytes: [Uint8Array] },
    getErrorFromCode: [Function: getErrorFromCode],
    getErrorFromName: [Function: getErrorFromName],
    isOnCluster: [Function: isOnCluster],
    availableGuards: [
      'botTax',           'solPayment',
      'tokenPayment',     'startDate',
      'thirdPartySigner', 'tokenGate',
      'gatekeeper',       'endDate',
      'allowList',        'mintLimit',
      'nftPayment',       'redeemedAmount',
      'addressGate',      'nftGate',
      'nftBurn',          'tokenBurn',
      'freezeSolPayment', 'freezeTokenPayment',
      'programGate'
    ]
  },
  logs: [
    'Program ComputeBudget111111111111111111111111111111 invoke [1]',
    'Program ComputeBudget111111111111111111111111111111 success',
    'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g invoke [1]',
    'Program log: Instruction: MintV2',
    'Program log: AnchorError thrown in program/src/utils.rs:95. Error Code: PublicKeyMismatch. Error Number: 6002. Error Message: Public key mismatch.',
    'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g consumed 27348 of 800000 compute units',
    'Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g failed: custom program error: 0x1772'
  ],
  code: 6002
}

mint.ts

// Docs: https://docs.metaplex.com/programs/candy-machine/candy-guards
// Test Code: https://github.com/metaplex-foundation/mpl-candy-machine/blob/main/clients/js/test/mintV2.test.ts

// Lib
import * as fs from 'fs';

// Candy Machine
import {
  mplCandyMachine,
  addConfigLines,
  create,
  mintV2,
  fetchCandyMachine,
} from "@metaplex-foundation/mpl-candy-machine";
import {
  setComputeUnitLimit,
} from "@metaplex-foundation/mpl-essentials";

// Token
import {
  createNft,
  TokenStandard,
} from "@metaplex-foundation/mpl-token-metadata";

// Umi
import {
  generateSigner,
  keypairIdentity,
  base58PublicKey,
  transactionBuilder,
  percentAmount,
  some,
  sol,
  dateTime,
} from "@metaplex-foundation/umi";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

const main = async () => {
  const endopoint = 'https://api.devnet.solana.com';
  const umi = createUmi(endopoint, 'confirmed')
    .use(mplCandyMachine());

  // CAUTION: Use Env file instead of below file for Production.
  const secretKey = new Uint8Array(JSON.parse(fs.readFileSync('./assets/id.json', 'utf8')));
  // Ref: https://github.com/metaplex-foundation/umi/blob/main/docs/publickeys-signers.md#keypairs
  const myKeypair = umi.eddsa.createKeypairFromSecretKey(secretKey);
  umi.use(keypairIdentity(myKeypair));

  // -------------------------------------
  //  Create Collection NFT
  // -------------------------------------
  const collectionUpdateAuthority = generateSigner(umi);
  const collectionMint = generateSigner(umi);
  // Ref: https://mpl-token-metadata-js-docs.vercel.app/functions/createNft.html
  await createNft(umi, {
    mint: collectionMint,
    authority: collectionUpdateAuthority,
    name: "My Collection NFT",
    uri: "https://arweave.net/yfVoS8kmFiM_XjfZOETgdCfrByKDyheSJ20nyam8_ag",
    sellerFeeBasisPoints: percentAmount(9.99, 2), // 9.99%
    isCollection: true,
  }).sendAndConfirm(umi);

  // -------------------------------------
  //  Candy Machine
  // -------------------------------------
  // Create a Candy Machine with guards.
  // Ref: https://docs.metaplex.com/programs/candy-machine/candy-machine-settings
  const candyMachine = generateSigner(umi);
  const createInstructions = await create(umi, {
    candyMachine,
    collectionMint: collectionMint.publicKey,
    collectionUpdateAuthority,
    tokenStandard: TokenStandard.NonFungible,
    sellerFeeBasisPoints: percentAmount(9.99, 2), // 9.99%
    itemsAvailable: 3, // Increase SOL cost per items. Check the cost on Devnet before launch.
    creators: [
      {
        address: umi.identity.publicKey,
        verified: true,
        percentageShare: 100,
      },
    ],
    configLineSettings: some({
      prefixName: "",
      nameLength: 32,
      prefixUri: "",
      uriLength: 200,
      // isSequential: indicates to whether a sequential index generation should be used during mint or not (recommended to set this value to false).
      isSequential: false,
    }),
    guards: {
      botTax: some({ lamports: sol(0.00321), lastInstruction: true }),
      startDate: some({ date: dateTime("2023-04-04T16:00:00Z") }),
      mintLimit: some({ id: 1, limit: 1 }),
      solPayment: some({ lamports: sol(0.00123), destination: umi.identity.publicKey }),
      // All other guards are disabled...
    },
  });
  await transactionBuilder().add(createInstructions).sendAndConfirm(umi);

  // -------------------------------------
  //  Insert Items
  // -------------------------------------
  // Ref: https://docs.metaplex.com/programs/candy-machine/inserting-items
  await addConfigLines(umi, {
    candyMachine: candyMachine.publicKey,
    index: 0,
    configLines: [
      { name: "My NFT #1", uri: "https://arweave.net/2V5Mx2dwnyrwlpHYPVshiyj_WtU7qIlPZzKs6nIwqw4" },
      { name: "My NFT #2", uri: "https://arweave.net/2dyAzm_p5kz8GYYLAzKj41KQL3vYT_kWyomJ3mTpBcA" },
      { name: "My NFT #3", uri: "https://arweave.net/tD5TVplWyzqYvM9feLbIKGqkuTzf0AIO0nLc4NDiiPk" },
    ],
  }).sendAndConfirm(umi);

  // -------------------------------------
  //  Mint NFT
  // -------------------------------------
  // This example temporarily uses minter as Umi's payer.
  // const minter = generateSigner(umi);
  const nftMint = generateSigner(umi);
  await transactionBuilder()
    .add(setComputeUnitLimit(umi, { units: 800_000 }))
    .add(
      mintV2(umi, {
        candyMachine: candyMachine.publicKey,
        // minter, // default to Umi's identity and payer
        nftMint,
        collectionMint: collectionMint.publicKey,
        collectionUpdateAuthority: collectionUpdateAuthority.publicKey,
        mintArgs: {
          mintLimit: some({ id: 1 }),
        },
      }),
    )
    .sendAndConfirm(umi);

  // -------------------------------------
  //  Fetch Candy Machine
  // -------------------------------------
  const candyMachineAccount = await fetchCandyMachine(
    umi,
    candyMachine.publicKey
  );

原因

Guardsで設定したsolPaymentをMint時に指定していないため。
「Public key mismatch」という内容だったため、Mintアドレスが間違ったのかと思ったが、今回のケースではMint時の引数が足りなかっただけだった。

Guardsで設定したら、mintArgsでも指定が必要(ということであってそう)。
Guardsはオンチェーンで管理しているので、トランザクション送信時にMinter側(Mintするユーザー)で一緒に設定(厳密にはInstruction)を投げないといけないっぽい。

対応

以下のようにmintArgsにsolPaymentを追加して解決。

  await transactionBuilder()
    .add(setComputeUnitLimit(umi, { units: 800_000 }))
    .add(
      mintV2(umi, {
        candyMachine: candyMachine.publicKey,
        // minter, // default to Umi's identity and payer
        nftMint,
        collectionMint: collectionMint.publicKey,
        collectionUpdateAuthority: collectionUpdateAuthority.publicKey,
        mintArgs: {
          mintLimit: some({ id: 1 }),
                    // 以下を追加!
          solPayment: some({ destination: umi.identity.publicKey }),
        },
      }),
    )
    .sendAndConfirm(umi);

参考

利用したソース 256hax GitHub