It'll be really helpful if someone guide me how to implement transaction request for NFT minting
1 Answer
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>
)
-
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 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.– johnJan 3 at 7:20
-
This Solana Pay tutorial has great explanations and walks through everything: pointer.gg/tutorials/solana-pay-irl-payments/…– johnJan 3 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– johnJan 3 at 7:26