Lesson 4: Minting and Transferring NFTs in Solidity Solang with PDAs
Step-by-Step Process for Minting and Transferring NFTs
mintTo
Function:
@mutableSigner(payer)
@mutableAccount(tokenAccount)
@account(owner)
@mutableAccount(mint)
@mutableAccount(pdaAccount)
function mintTo() external {
// Function body
}
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
);
_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
);
_removeMintAuthority
to revoke minting rights: _removeMintAuthority(
tx.accounts.mint.key, // Mint account
tx.accounts.pdaAccount.key // PDA account
);
_mintTo
Function:
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);
}
Custom _removeMintAuthority
Function:
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);
}
@mutableAccount(from)
@mutableAccount(to)
@signer(owner)
function transferNft() external {
// Transfer logic
}
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
);
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._mintTo
and _removeMintAuthority
are tailored to use a PDA as the mint authority, aligning with Solana's security model.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.