pda-mint-authority.sol 7.7 KB

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