123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import "../libraries/spl_token.sol";
- import "solana";
- @program_id("J2eUKE878XKXJZaP7vXwxgWnWnNQMqHSkMPoRFQwa86b")
- contract pda_mint_authority {
- bytes1 bump; // stores the bump for the pda address
- @payer(payer)
- @seed("mint_authority") // hard-coded seed
- constructor(
- @bump bytes1 _bump // bump for the pda address
- ) {
- // Independently derive the PDA address from the seeds, bump, and programId
- (address pda, bytes1 pdaBump) = try_find_program_address(["mint_authority"], type(pda_mint_authority).program_id);
- // Verify that the bump passed to the constructor matches the bump derived from the seeds and programId
- // This ensures that only the canonical pda address can be used to create the account (first bump that generates a valid pda address)
- require(pdaBump == _bump, 'INVALID_BUMP');
- bump = _bump;
- }
- @mutableSigner(payer) // payer account
- @mutableSigner(mint) // mint account to be created
- @mutableAccount(metadata) // metadata account to be created
- @account(mintAuthority) // mint authority for the mint account
- @account(rentAddress)
- @account(metaplexId)
- function createTokenMint(
- address freezeAuthority, // freeze authority for the mint account
- uint8 decimals, // decimals for the mint account
- string name, // name for the metadata account
- string symbol, // symbol for the metadata account
- string uri // uri for the metadata account
- ) external {
- // Invoke System Program to create a new account for the mint account and,
- // Invoke Token Program to initialize the mint account
- // Set mint authority, freeze authority, and decimals for the mint account
- SplToken.create_mint(
- tx.accounts.payer.key, // payer account
- tx.accounts.mint.key, // mint account
- tx.accounts.mintAuthority.key, // mint authority
- freezeAuthority, // freeze authority
- decimals // decimals
- );
- // Invoke Metadata Program to create a new account for the metadata account
- _createMetadataAccount(
- tx.accounts.metadata.key, // metadata account
- tx.accounts.mint.key, // mint account
- tx.accounts.mintAuthority.key, // mint authority
- tx.accounts.payer.key, // payer
- tx.accounts.payer.key, // update authority (of the metadata account)
- name, // name
- symbol, // symbol
- uri, // uri (off-chain metadata json)
- tx.accounts.rentAddress.key,
- tx.accounts.metaplexId.key
- );
- }
- // Create metadata account, must reimplement manually to sign with PDA, which is the mint authority
- function _createMetadataAccount(
- address metadata, // metadata account address
- address mint, // mint account address
- address mintAuthority, // mint authority
- address payer, // payer
- address updateAuthority, // update authority for the metadata account
- string name, // token name
- string symbol, // token symbol
- string uri, // token uri
- address rentAddress,
- address metaplexId
- ) 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(mintAuthority == pda, 'INVALID_PDA');
- DataV2 data = DataV2({
- name: name,
- symbol: symbol,
- uri: uri,
- sellerFeeBasisPoints: 0,
- creatorsPresent: false,
- collectionPresent: false,
- usesPresent: false
- });
- CreateMetadataAccountArgsV3 args = CreateMetadataAccountArgsV3({
- data: data,
- isMutable: true,
- collectionDetailsPresent: false
- });
- AccountMeta[7] metas = [
- AccountMeta({pubkey: metadata, is_writable: true, is_signer: false}),
- AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
- AccountMeta({pubkey: mintAuthority, is_writable: false, is_signer: true}),
- AccountMeta({pubkey: payer, is_writable: true, is_signer: true}),
- AccountMeta({pubkey: updateAuthority, is_writable: false, is_signer: false}),
- AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false}),
- AccountMeta({pubkey: rentAddress, is_writable: false, is_signer: false})
- ];
- bytes1 discriminator = 33;
- bytes instructionData = abi.encode(discriminator, args);
- metaplexId.call{accounts: metas, seeds: [["mint_authority", abi.encode(_bump)]]}(instructionData);
- }
- struct CreateMetadataAccountArgsV3 {
- DataV2 data;
- bool isMutable;
- bool collectionDetailsPresent; // To handle Rust Option<> in Solidity
- }
- struct DataV2 {
- string name;
- string symbol;
- string uri;
- uint16 sellerFeeBasisPoints;
- bool creatorsPresent; // To handle Rust Option<> in Solidity
- bool collectionPresent; // To handle Rust Option<> in Solidity
- bool usesPresent; // To handle Rust Option<> in Solidity
- }
- @mutableSigner(payer)
- @mutableAccount(tokenAccount)
- @account(owner)
- @mutableAccount(mint)
- @mutableAccount(pdaAccount)
- function mintTo() external {
- // Create an associated token account for the owner to receive the minted token
- SplToken.create_associated_token_account(
- tx.accounts.payer.key, // payer account
- tx.accounts.tokenAccount.key, // associated token account address
- tx.accounts.mint.key, // mint account
- tx.accounts.owner.key // owner account
- );
- // Mint 1 token to the associated token account
- _mintTo(
- tx.accounts.mint.key, // mint account
- tx.accounts.tokenAccount.key, // token account
- 1, // amount
- tx.accounts.pdaAccount.key
- );
- // // Remove mint authority from mint account
- _removeMintAuthority(
- tx.accounts.mint.key, // mint
- tx.accounts.pdaAccount.key
- );
- }
- // Invoke the token program to mint tokens to a token account, using a PDA as the mint authority
- 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);
- }
- 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);
- }
- }
|