Solana Metaplexのactions.sendTokenで「Owner cannot sign」「wallet.signTransaction is not a function」エラー

@metaplex/js/actions/sendToken を試していたときに発生したエラー。

「Owner cannot sign」エラー

現象

以下のエラーが発生。

/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/node_modules/@solana/spl-token/client/token.js:2285
      throw new Error(`Owner cannot sign: ${owner.toString()}`);
            ^
Error: Owner cannot sign: 8Eh1svVoaNTWY5FfEPtsrQueYc8Zs51yUmv1Cqz4LVnS
    at Function.getAssociatedTokenAddress (/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/node_modules/@solana/spl-token/client/token.js:2285:13)
    at /Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/src/actions/sendToken.ts:33:31
    at Generator.next (<anonymous>)
    at /Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/lib/index.cjs.js:88:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/lib/index.cjs.js:84:12)
    at Object.sendToken (/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/src/actions/sendToken.ts:31:20)
    at /Users/user/Desktop/temp/test-mint-and-transfer-nft/src/modules/transferNft.ts:48:38
    at Generator.next (<anonymous>)
    at fulfilled (/Users/user/Desktop/temp/test-mint-and-transfer-nft/src/modules/transferNft.ts:5:58)

原因

desinationの指定が誤っている。自分の場合は、トークンアカウントを指定していた。

  const fromWallet = keypair;
  const toWallet = Keypair.generate();
  const mint = new PublicKey(mintNftAddress);

  const toTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    fromWallet,
    mint,
    toWallet.publicKey
  );

  const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      fromWallet,
      mint,
      fromWallet.publicKey
  );

  const signature_tx = await actions.sendToken({
    connection: connection,
    wallet: new NodeWallet(fromWallet),
    source: fromTokenAccount.address,
    destination: toTokenAccount.address, // ←これが誤り
    mint: mint,
    amount: 1,
  });

以下の記載があるため、おそらくトークンアカウントを指定してはならず、親アカウントを指定する必要がある。

2. If the associated token account doesn't exist, it will be created

sendToken

destination: PublicKey
Destination wallet address

destination

@solana/spl-token transfer だと、宛先はトークンアカウントを指定するため、同じと勘違いしていた。

対応

親カウントを指定する。

  const signature_tx = await actions.sendToken({
    connection: connection,
    wallet: new NodeWallet(fromWallet),
    source: fromTokenAccount.address,
    destination: toWallet.publicKey, // ←親アカウントを指定
    mint: mint,
    amount: 1,
  });

「wallet.signTransaction is not a function」エラー

現象

以下のエラーが発生。

/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/lib/index.cjs.js:85
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
                                                         ^
TypeError: wallet.signTransaction is not a function
    at /Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/src/actions/transactions.ts:27:21
    at Generator.next (<anonymous>)
    at fulfilled (/Users/user/Desktop/temp/test-mint-and-transfer-nft/node_modules/@metaplex/js/lib/index.cjs.js:85:58)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

原因

walletの指定が誤り。Keypairの指定は不可。

  const fromWallet = keypair;
  const toWallet = Keypair.generate();
  const mint = new PublicKey(mintNftAddress);

  const toTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    fromWallet,
    mint,
    toWallet.publicKey
  );

  const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      fromWallet,
      mint,
      fromWallet.publicKey
  );

  const signature_tx = await actions.sendToken({
    connection: connection,
    wallet: fromWallet, // ←これが誤り
    source: fromTokenAccount.address,
    destination: toWallet.publicKey,
    mint: mint,
    amount: 1,
  });

本件も @solana/spl-token transfer だと、Keypairを指定すると同じものと勘違いした。

対応

Metaplexが用意しているNodeWalletを使う。

    import { actions, NodeWallet } from '@metaplex/js'; // ←NodeWalletを使う

  const signature_tx = await actions.sendToken({
    connection: connection,
    wallet: new NodeWallet(fromWallet), // ←NodeWalletを使う
    source: fromTokenAccount.address,
    destination: toWallet.publicKey,
    mint: mint,
    amount: 1,
  });