This part details the step-by-step algorithm for implementing a Solidity Solang program for NFT minting and transfer, where the NFT mint authority is a Program Derived Address (PDA).
illustration
b
Mint NFT And Transfer To Another User

Lesson 4: Minting and Transferring NFTs in Solidity Solang with PDAs

Step-by-Step Process for Minting and Transferring NFTs

  1. Creating the mintTo Function:
    • Define an external function to mint NFTs:
     @mutableSigner(payer)
     @mutableAccount(tokenAccount)
     @account(owner)
     @mutableAccount(mint)
     @mutableAccount(pdaAccount)
     function mintTo() external {
         // Function body
     }
  1. Creating an Associated Token Account (ATA):
    • Implement ATA creation for receiving the minted token:
     SplToken.create_associated_token_account(
         tx.accounts.payer.key, // Payer's account
         tx.accounts.tokenAccount.key, // Token account address
         tx.accounts.mint.key, // Mint account
         tx.accounts.owner.key // Owner's account
     );
  1. Minting the Token:
    • Use a custom _mintTo method for minting:
     _mintTo(
         tx.accounts.mint.key, // Mint account
         tx.accounts.tokenAccount.key, // Token account
         1, // Amount to mint
         tx.accounts.pdaAccount.key // PDA account
     );
  1. Removing Mint Authority:
    • Call _removeMintAuthority to revoke minting rights:
     _removeMintAuthority(
         tx.accounts.mint.key, // Mint account
         tx.accounts.pdaAccount.key // PDA account
     );
  1. Custom _mintTo Function:
    • Define a private method to handle minting with PDA:
     function _mintTo(address mint, address account, uint64 amount, address pdaAccount) private {
     // Independently derive the PDA address from the seeds, bump, and programId
        (address pda, bytes1 _bump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
        require(pdaAccount == pda, 'INVALID_PDA');

        // Prepare instruction data
        bytes instructionData = new bytes(9);
        instructionData[0] = uint8(7); // MintTo instruction index
        instructionData.writeUint64LE(amount, 1); // Amount to mint

        // Prepare accounts required by instruction
        AccountMeta[3] metas = [
            AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
            AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
            AccountMeta({pubkey: pda, is_writable: true, is_signer: true}) // mint authority
        ];

        // Invoke the token program with prepared accounts and instruction data
        SplToken.tokenProgramId.call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
     }
  1. Custom _removeMintAuthority Function:

    • Implement a private method to remove mint authority:
     function _removeMintAuthority(address mintAccount, address pdaAccount) private {
         // Independently derive the PDA address from the seeds, bump, and programId
        (address pda, bytes1 _bump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
        require(pdaAccount == pda, 'INVALID_PDA');

		AccountMeta[2] metas = [
			AccountMeta({pubkey: mintAccount, is_signer: false, is_writable: true}),
			AccountMeta({pubkey: pda, is_signer: true, is_writable: false}) // mint authority
		];

		bytes instructionData = new bytes(9);
		instructionData[0] = uint8(6); // SetAuthority instruction index
		instructionData[1] = uint8(0); // AuthorityType::MintTokens
		instructionData[3] = 0;

        // Invoke the token program with prepared accounts and instruction data
        SplToken.tokenProgramId.call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
        }
  1. Creating the Transfer Function:
    • Define a method for NFT transfer:
     @mutableAccount(from)
     @mutableAccount(to)
     @signer(owner)
     function transferNft() external {
         // Transfer logic
     }
  1. Using SPL Token Transfer:
    • Implement the transfer using SPL token library:
     SplToken.transfer(
         tx.accounts.from.key, // From token account
         tx.accounts.to.key, // To token account
         tx.accounts.owner.key, // Owner account
         1 // Number of tokens to transfer
    );
  • Minting Process: The mintTo function creates an ATA for the NFT owner, mints the NFT to this account, and then removes the mint authority to prevent further minting.
  • Reimplemented Methods: _mintTo and _removeMintAuthority are tailored to use a PDA as the mint authority, aligning with Solana's security model.
  • NFT Transfer: The transferNft function facilitates the transfer of the minted NFT to another user, employing standard SPL token operations.

By integrating these methods, the Solidity Solang program achieves a secure and efficient process for minting and transferring NFTs on the Solana blockchain, utilizing PDAs for enhanced control and security.