0

I am trying to create an object compatible with metaplex's CreateMetadataAccountV3InstructionAccounts to be used in createMetadataAccountV3 function. The CreateMetadataAccountV3InstructionAccounts has below type in Metaplex

export type CreateMetadataAccountV3InstructionAccounts = {
    /** Metadata key (pda of ['metadata', program id, mint id]) */
    metadata?: PublicKey | Pda;
    /** Mint of token asset */
    mint: PublicKey | Pda;
    /** Mint authority */
    mintAuthority: Signer;
    /** payer */
    payer?: Signer;
    /** update authority info */
    updateAuthority?: PublicKey | Pda | Signer;
    /** System program */
    systemProgram?: PublicKey | Pda;
    /** Rent info */
    rent?: PublicKey | Pda;
};

The mintAuthority expects a Signer interface type which is defined below:

export interface Signer {
    /** The public key of the Signer. */
    readonly publicKey: PublicKey;
    /** Signs the given message. */
    readonly signMessage: (message: Uint8Array) => Promise<Uint8Array>;
    /** Signs the given transaction. */
    readonly signTransaction: (transaction: Transaction) => Promise<Transaction>;
    /** Signs all the given transactions at once. */
    readonly signAllTransactions: (transactions: Transaction[]) => Promise<Transaction[]>;
}

Now, below is my code how I tried to build the data that can be used in createMetadataAccountV3 function

const MY_TOKEN_METADATA: CreateMetadataAccountV3InstructionArgs = {
    data: {
        name: "Testing",
        symbol: "TST",
        uri: "https://arweave.net/v2PSXJy95LrpkcFytCLw7BCZw0lSNgbfM53VyfImExx",
        sellerFeeBasisPoints: 0,
        collection: null,
        creators: [
            {address: fromWeb3JsPublicKey(userWallet.publicKey), verified: true, share: 100}
        ],
        uses: null
    },
    isMutable: false,
    collectionDetails: null
}

const signer = {
        publicKey: fromWeb3JsPublicKey(userWallet.publicKey),
        signMessage: null,
        signTransaction: null,
        signAllTransactions: null
    }
    const accounts: CreateMetadataAccountV3InstructionAccounts = {
        metadata: fromWeb3JsPublicKey(metadataPDA),
        mint: fromWeb3JsPublicKey(metadataPDA), 
        payer: signer,
        mintAuthority: signer,
        updateAuthority: fromWeb3JsPublicKey(userWallet.publicKey)
    }

But in payer: signer, mintAuthority: signer

I am getting below error:

Type '{ publicKey: PublicKey; signMessage: null; signTransaction: null; signAllTransactions: null; }' is not assignable to type 'Signer'.
Types of property 'signMessage' are incompatible.
Type 'null' is not assignable to type '(message: Uint8Array) => Promise<Uint8Array>'.

How can I create the Signer correctly?

Below is the whole code

import { Transaction, SystemProgram, Keypair, Connection, PublicKey } from "@solana/web3.js";
import { MINT_SIZE, TOKEN_PROGRAM_ID, createInitializeMintInstruction, getMinimumBalanceForRentExemptMint, getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createMintToInstruction } from '@solana/spl-token';
import { DataV2, createMetadataAccountV3, CreateMetadataAccountV3InstructionAccounts, CreateMetadataAccountV3InstructionArgs } from '@metaplex-foundation/mpl-token-metadata';
import { bundlrStorage, keypairIdentity, Metaplex, UploadMetadataInput } from '@metaplex-foundation/js';
import secret from './guideSecret.json';
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { fromWeb3JsPublicKey, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'

const endpoint = 'https://warmhearted-hardworking-county.solana-devnet.quiknode.pro/cb4264034a8b3997510f99f8f2006389a4b57754/'; //Replace with your RPC Endpoint
const solanaConnection = new Connection(endpoint);
const userWallet = Keypair.fromSecretKey(new Uint8Array(secret));

const MINT_CONFIG = {
    numDecimals: 6,
    numberTokens: 1337
}

const MY_TOKEN_METADATA: CreateMetadataAccountV3InstructionArgs = {
    data: {
        name: "Testing",
        symbol: "TST",
        uri: "https://arweave.net/v2PSXJy95LrpkcFytCLw7BCZw0lSNgoFM58VyLImExc",
        sellerFeeBasisPoints: 0,
        collection: null,
        creators: [
            {address: fromWeb3JsPublicKey(userWallet.publicKey), verified: true, share: 100}
        ],
        uses: null
    },
    isMutable: false,
    collectionDetails: null
}

// const ON_CHAIN_METADATA = {
//     name: MY_TOKEN_METADATA.name, 
//     symbol: MY_TOKEN_METADATA.symbol,
//     uri: 'TO_UPDATE_LATER',
//     sellerFeeBasisPoints: 0,
// } as DataV2;

/**
 * 
 * @param wallet Solana Keypair
 * @param tokenMetadata Metaplex Fungible Token Standard object 
 * @returns Arweave url for our metadata json file
 */
const uploadMetadata = async(wallet: Keypair, tokenMetadata: UploadMetadataInput):Promise<string> => {
    const metaplex = Metaplex.make(solanaConnection)
        .use(keypairIdentity(wallet));
    
    const { uri } = await metaplex.nfts().uploadMetadata(tokenMetadata);
    return uri;
}

const createNewMintTransaction = async (connection:Connection, payer:Keypair, mintKeypair: Keypair, destinationWallet: PublicKey, mintAuthority: PublicKey, freezeAuthority: PublicKey)=>{
    //Get the minimum lamport balance to create a new account and avoid rent payments
    const requiredBalance = await getMinimumBalanceForRentExemptMint(connection);
    const metaplex = Metaplex.make(solanaConnection);
    const umi = createUmi(connection);
    //metadata account associated with mint
    const metadataPDA = await metaplex.nfts().pdas().metadata({mint: mintKeypair.publicKey});
    const signer = {
        publicKey: fromWeb3JsPublicKey(userWallet.publicKey),
        signMessage: null,
        signTransaction: null,
        signAllTransactions: null
    }
    const accounts: CreateMetadataAccountV3InstructionAccounts = {
        metadata: fromWeb3JsPublicKey(metadataPDA),
        mint: fromWeb3JsPublicKey(metadataPDA), 
        payer: signer,
        mintAuthority: signer,
        updateAuthority: fromWeb3JsPublicKey(userWallet.publicKey)
    }
    //get associated token account of your wallet
    const tokenATA = await getAssociatedTokenAddress(mintKeypair.publicKey, destinationWallet);
    const CreateMetadataAccountV3Args = {
        metadata: metadataPDA,
        mint: mintKeypair.publicKey,
        mintAuthority: mintAuthority,
        payer: payer,
        updateAuthority: mintAuthority,
        data: {
            name: "myname",
            symbol: "exp",
            uri: "example_uri.com",
            sellerFeeBasisPoints: 0,
            creators: null,
            collection: null,
            uses: null
        },
        isMutable: false,
        collectionDetails: null
    }
    const createNewTokenTransaction = new Transaction().add(
        SystemProgram.createAccount({
            fromPubkey: payer.publicKey,
            newAccountPubkey: mintKeypair.publicKey,
            space: MINT_SIZE,
            lamports: requiredBalance,
            programId: TOKEN_PROGRAM_ID,
        }),
        createInitializeMintInstruction(
          mintKeypair.publicKey, //Mint Address
          MINT_CONFIG.numDecimals, //Number of Decimals of New mint
          mintAuthority, //Mint Authority
          freezeAuthority, //Freeze Authority
          TOKEN_PROGRAM_ID),
        createAssociatedTokenAccountInstruction(
          payer.publicKey, //Payer 
          tokenATA, //Associated token account 
          payer.publicKey, //token owner
          mintKeypair.publicKey, //Mint
        ),
        createMintToInstruction(
          mintKeypair.publicKey, //Mint
          tokenATA, //Destination Token Account
          mintAuthority, //Authority
          MINT_CONFIG.numberTokens * Math.pow(10, MINT_CONFIG.numDecimals),//number of tokens
        ),
        createMetadataAccountV3(umi, CreateMetadataAccountV3Args)
    );

    return createNewTokenTransaction;
}

const main = async() => {
    console.log(`---STEP 1: Uploading MetaData---`);
    let metadataUri = await uploadMetadata(userWallet, MY_TOKEN_METADATA);
    //ON_CHAIN_METADATA.uri = metadataUri;

    console.log(`---STEP 2: Creating Mint Transaction---`);
    let mintKeypair = Keypair.generate();   
    console.log(`New Mint Address: `, mintKeypair.publicKey.toString());

    const newMintTransaction:Transaction = await createNewMintTransaction(
        solanaConnection,
        userWallet,
        mintKeypair,
        userWallet.publicKey,
        userWallet.publicKey,
        userWallet.publicKey
    );

    console.log(`---STEP 3: Executing Mint Transaction---`);
    const transactionId =  await solanaConnection.sendTransaction(newMintTransaction, [userWallet, mintKeypair]);
    console.log(`Transaction ID: `, transactionId);
    //console.log(`Succesfully minted ${MINT_CONFIG.numberTokens} ${ON_CHAIN_METADATA.symbol} to ${userWallet.publicKey.toString()}.`);
    console.log(`View Transaction: https://explorer.solana.com/tx/${transactionId}?cluster=devnet`);
    console.log(`View Token Mint: https://explorer.solana.com/address/${mintKeypair.publicKey.toString()}?cluster=devnet`)
}


main();

1 Answer 1

0

The error seems to say what's wrong:

Type '{ publicKey: PublicKey; signMessage: null; signTransaction: null; signAllTransactions: null; }' is not assignable to type 'Signer'.
Types of property 'signMessage' are incompatible.
Type 'null' is not assignable to type '(message: Uint8Array) => Promise<Uint8Array>'.

the signMessage field is supposed to be a function that takes a Uint8Array and returns a Promise<Uint8Array>. If you need to mock this, you could add a function that just returns the message back without signing, ie:

const signer = {
    publicKey: fromWeb3JsPublicKey(userWallet.publicKey),
    signMessage: (msg) => Promise.resolve(msg),
    signTransaction: null,
    signAllTransactions: null
}

Note that this might still fail because of the other fields, which likely can't be null.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.