2

It'll be really helpful if someone guide me how to implement transaction request for NFT minting

1 Answer 1

3

Here is a reference nft-create example from the solana-pay repo: https://github.com/solana-labs/solana-pay/tree/master/examples/nft-create

The API to build the Solana Pay transaction request is here: https://github.com/solana-labs/solana-pay/blob/master/examples/nft-create/pages/api/checkout.ts

Specifically, the example shows how to use the metaplex sdk to create/mint the NFT and add additional instructions to the same transaction:

async function postImpl(account: PublicKey): Promise<PostResponse> {
  const connection = new Connection(ENDPOINT)

  // Get the shop keypair from the environment variable
  const shopPrivateKey = process.env.SHOP_PRIVATE_KEY
  if (!shopPrivateKey) throw new Error('SHOP_PRIVATE_KEY not found')
  const shopKeypair = Keypair.fromSecretKey(base58.decode(shopPrivateKey))

  // Initialise Metaplex with our shop keypair
  const metaplex = Metaplex
    .make(connection)
    .use(keypairIdentity(shopKeypair))

  const nfts = metaplex.nfts()

  // The mint needs to sign the transaction, so we generate a new keypair for it
  const mintKeypair = Keypair.generate()

  // Create a transaction builder to create the NFT
  const transactionBuilder = await nfts.builders().create({
    uri: METADATA_URI, // use our metadata
    name: NFT_NAME,
    tokenOwner: account, // NFT is minted to the wallet submitting the transaction (buyer)
    updateAuthority: shopKeypair, // we retain update authority
    sellerFeeBasisPoints: 100, // 1% royalty
    useNewMint: mintKeypair, // we pass our mint in as the new mint to use
  })

  // Next we create an instruction to transfer USDC from the buyer to the shop
  // This will be added to the create NFT transaction

  // Get the buyer's USDC address
  const fromUsdcAddress = await getOrCreateAssociatedTokenAccount(
    connection,
    shopKeypair,
    USDC_ADDRESS,
    account,
  )

  // Get the shop's USDC address
  const toUsdcAddress = await getOrCreateAssociatedTokenAccount(
    connection,
    shopKeypair,
    USDC_ADDRESS,
    shopKeypair.publicKey,
  )

  const usdcMint = await getMint(connection, USDC_ADDRESS)
  const decimals = usdcMint.decimals

  const usdcTransferInstruction = createTransferCheckedInstruction(
    fromUsdcAddress.address, // from USDC address
    USDC_ADDRESS, // USDC mint address
    toUsdcAddress.address, // to USDC address
    account, // owner of the from USDC address (the buyer)
    PRICE_USDC * (10 ** decimals), // multiply by 10^decimals
    decimals
  )

  // Create a guest identity for buyer, so they will be a required signer for the transaction
  const identitySigner = new GuestIdentityDriver(account)

  // Add the USDC payment to the NFT transaction
  transactionBuilder.prepend({
    instruction: usdcTransferInstruction,
    signers: [identitySigner]
  })

  // transactionBuilder.setFeePayer(payerKeypair)

  // Convert to transaction
  const latestBlockhash = await connection.getLatestBlockhash()
  const transaction = await transactionBuilder.toTransaction(latestBlockhash)

  // Partially sign the transaction, as the shop and the mint
  // The account is also a required signer, but they'll sign it with their wallet after we return it
  transaction.sign(shopKeypair, mintKeypair)

  // Serialize the transaction and convert to base64 to return it
  const serializedTransaction = transaction.serialize({
    requireAllSignatures: false // account is a missing signature
  })
  const base64 = serializedTransaction.toString('base64')

  const message = "Please approve the transaction to mint your golden ticket!"

  // Return the serialized transaction
  return {
    transaction: base64,
    message,
  }
}

The Solana Pay transaction request URL is used here: https://github.com/solana-labs/solana-pay/blob/master/examples/nft-create/pages/index.tsx

It shows how to encode and display the Solana Pay URL as a QR Code:

  const mintQrRef = useRef<HTMLDivElement>()

  // Generate the Solana Pay QR code
  // This is a transaction request, with our checkout API as the link
  // We can only generate a QR code on the client, so do it in the useEffect
  useEffect(() => {
    const { location } = window
    const apiUrl = `${location.protocol}//${location.host}/api/checkout`

    const mintUrlFields: TransactionRequestURLFields = {
      link: new URL(apiUrl),
    }
    const mintUrl = encodeURL(mintUrlFields)
    const mintQr = createQR(mintUrl, 400, 'transparent')

    // Set the generated QR code on the QR ref element
    if (mintQrRef.current) {
      mintQrRef.current.innerHTML = ''
      mintQr.append(mintQrRef.current)
    }
  }, [])

  return (
      <div ref={mintQrRef} />
  )

Or use the transaction request URL with a button and connected wallet:

const { publicKey, sendTransaction } = useWallet()
  
// Handler for performing the transaction with a connected wallet
async function buy(e: React.MouseEvent) {
  e.preventDefault();
  
  // Fetch the transaction from our checkout API
  // We pass the connected wallet as `account`
  const response = await fetch('/api/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ account: publicKey.toBase58() })
  });
  
  const responseBody = await response.json() as CheckoutPostResponse | CheckoutPostError;
  
  if ('error' in responseBody) {
    const error = responseBody.error
    console.error(error)
    alert(`Error fetching transaction: ${error}`)
    return
  }
  
  // We receive the transaction serialized to base64, deserialize it to send
  const transaction = Transaction.from(Buffer.from(responseBody.transaction, 'base64'));
  try {
    await sendTransaction(transaction, connection)
    alert('Purchase complete!')
  } catch (error) {
    console.error(error)
    alert(`Error sending transaction: ${error}`)
  }
}
    
return (
  <button
    type="button"
    disabled={!publicKey}
    onClick={buy}
  >
    Buy now
  </button>
)
4
  • Thank you so much for the reference. One thing I need to ask is what is the correct way (flow) of handling nft mint with qr scan ? Jan 3, 2023 at 6:07
  • The Solana Pay transaction request URLs handle GET and POST requests. The URL is encoded in a QR Code, which can be scanned by a wallet. When the wallet scans the QR Code, it makes a GET request and displays the 'icon' and 'label' to the user. The wallet then makes a POST request, sending the public key of the wallet as the request body. The public key is used to create the transaction, which is then sent back to the wallet for the user to review and confirm before sending it to the network.
    – john
    Jan 3, 2023 at 7:20
  • This Solana Pay tutorial has great explanations and walks through everything: pointer.gg/tutorials/solana-pay-irl-payments/…
    – john
    Jan 3, 2023 at 7:24
  • Buildspace also has a tutorial you can reference (although I recommend looking through the pointer.gg one first): buildspace.so/builds/solana-pay
    – john
    Jan 3, 2023 at 7:26

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.