pda-mint-authority.sol 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import "../libraries/spl_token.sol";
  2. import "solana";
  3. @program_id("J2eUKE878XKXJZaP7vXwxgWnWnNQMqHSkMPoRFQwa86b")
  4. contract pda_mint_authority {
  5. bytes1 bump; // stores the bump for the pda address
  6. @payer(payer)
  7. @seed("mint_authority") // hard-coded seed
  8. constructor(
  9. @bump bytes1 _bump // bump for the pda address
  10. ) {
  11. // Independently derive the PDA address from the seeds, bump, and programId
  12. (address pda, bytes1 pdaBump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
  13. // Verify that the bump passed to the constructor matches the bump derived from the seeds and programId
  14. // This ensures that only the canonical pda address can be used to create the account (first bump that generates a valid pda address)
  15. require(pdaBump == _bump, 'INVALID_BUMP');
  16. bump = _bump;
  17. }
  18. function createTokenMint(
  19. address payer, // payer account
  20. address mint, // mint account to be created
  21. address mintAuthority, // mint authority for the mint account
  22. address freezeAuthority, // freeze authority for the mint account
  23. address metadata, // metadata account to be created
  24. uint8 decimals, // decimals for the mint account
  25. string name, // name for the metadata account
  26. string symbol, // symbol for the metadata account
  27. string uri // uri for the metadata account
  28. ) public {
  29. // Invoke System Program to create a new account for the mint account and,
  30. // Invoke Token Program to initialize the mint account
  31. // Set mint authority, freeze authority, and decimals for the mint account
  32. SplToken.create_mint(
  33. payer, // payer account
  34. mint, // mint account
  35. mintAuthority, // mint authority
  36. freezeAuthority, // freeze authority
  37. decimals // decimals
  38. );
  39. // Invoke Metadata Program to create a new account for the metadata account
  40. _createMetadataAccount(
  41. metadata, // metadata account
  42. mint, // mint account
  43. mintAuthority, // mint authority
  44. payer, // payer
  45. payer, // update authority (of the metadata account)
  46. name, // name
  47. symbol, // symbol
  48. uri // uri (off-chain metadata json)
  49. );
  50. }
  51. // Create metadata account, must reimplement manually to sign with PDA, which is the mint authority
  52. function _createMetadataAccount(
  53. address metadata, // metadata account address
  54. address mint, // mint account address
  55. address mintAuthority, // mint authority
  56. address payer, // payer
  57. address updateAuthority, // update authority for the metadata account
  58. string name, // token name
  59. string symbol, // token symbol
  60. string uri // token uri
  61. ) private {
  62. // // Independently derive the PDA address from the seeds, bump, and programId
  63. (address pda, bytes1 _bump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
  64. require(address(this) == pda, 'INVALID_PDA');
  65. DataV2 data = DataV2({
  66. name: name,
  67. symbol: symbol,
  68. uri: uri,
  69. sellerFeeBasisPoints: 0,
  70. creatorsPresent: false,
  71. collectionPresent: false,
  72. usesPresent: false
  73. });
  74. CreateMetadataAccountArgsV3 args = CreateMetadataAccountArgsV3({
  75. data: data,
  76. isMutable: true,
  77. collectionDetailsPresent: false
  78. });
  79. AccountMeta[7] metas = [
  80. AccountMeta({pubkey: metadata, is_writable: true, is_signer: false}),
  81. AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
  82. AccountMeta({pubkey: mintAuthority, is_writable: false, is_signer: true}),
  83. AccountMeta({pubkey: payer, is_writable: true, is_signer: true}),
  84. AccountMeta({pubkey: updateAuthority, is_writable: false, is_signer: false}),
  85. AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false}),
  86. AccountMeta({pubkey: address"SysvarRent111111111111111111111111111111111", is_writable: false, is_signer: false})
  87. ];
  88. bytes1 discriminator = 33;
  89. bytes instructionData = abi.encode(discriminator, args);
  90. address"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s".call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
  91. }
  92. struct CreateMetadataAccountArgsV3 {
  93. DataV2 data;
  94. bool isMutable;
  95. bool collectionDetailsPresent; // To handle Rust Option<> in Solidity
  96. }
  97. struct DataV2 {
  98. string name;
  99. string symbol;
  100. string uri;
  101. uint16 sellerFeeBasisPoints;
  102. bool creatorsPresent; // To handle Rust Option<> in Solidity
  103. bool collectionPresent; // To handle Rust Option<> in Solidity
  104. bool usesPresent; // To handle Rust Option<> in Solidity
  105. }
  106. function mintTo(address payer, address tokenAccount, address mint, address owner) public {
  107. // Create an associated token account for the owner to receive the minted token
  108. SplToken.create_associated_token_account(
  109. payer, // payer account
  110. tokenAccount, // associated token account address
  111. mint, // mint account
  112. owner // owner account
  113. );
  114. // Mint 1 token to the associated token account
  115. _mintTo(
  116. mint, // mint account
  117. tokenAccount, // token account
  118. 1 // amount
  119. );
  120. // Remove mint authority from mint account
  121. _removeMintAuthority(
  122. mint // mint
  123. );
  124. }
  125. // Invoke the token program to mint tokens to a token account, using a PDA as the mint authority
  126. function _mintTo(address mint, address account, uint64 amount) private {
  127. // Independently derive the PDA address from the seeds, bump, and programId
  128. (address pda, bytes1 _bump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
  129. require(address(this) == pda, 'INVALID_PDA');
  130. // Prepare instruction data
  131. bytes instructionData = new bytes(9);
  132. instructionData[0] = uint8(7); // MintTo instruction index
  133. instructionData.writeUint64LE(amount, 1); // Amount to mint
  134. // Prepare accounts required by instruction
  135. AccountMeta[3] metas = [
  136. AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
  137. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  138. AccountMeta({pubkey: pda, is_writable: true, is_signer: true}) // mint authority
  139. ];
  140. // Invoke the token program with prepared accounts and instruction data
  141. SplToken.tokenProgramId.call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
  142. }
  143. function _removeMintAuthority(address mintAccount) private {
  144. // Independently derive the PDA address from the seeds, bump, and programId
  145. (address pda, bytes1 _bump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
  146. require(address(this) == pda, 'INVALID_PDA');
  147. AccountMeta[2] metas = [
  148. AccountMeta({pubkey: mintAccount, is_signer: false, is_writable: true}),
  149. AccountMeta({pubkey: pda, is_signer: true, is_writable: false}) // mint authority
  150. ];
  151. bytes instructionData = new bytes(9);
  152. instructionData[0] = uint8(6); // SetAuthority instruction index
  153. instructionData[1] = uint8(0); // AuthorityType::MintTokens
  154. instructionData[3] = 0;
  155. // Invoke the token program with prepared accounts and instruction data
  156. SplToken.tokenProgramId.call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
  157. }
  158. }