Minting + Burning SPL Tokens

SPL tokens are Solana’s fungible token standard, and they can be thought of as the equivalent of ERC20s in Ethereum.

  • Create Mint

    pub(crate) async fn create_mint(client: &TestClient, payer: &Keypair) -> anyhow::Result<Keypair> {
        let mint_account = Keypair::new();
    
        let latest_blockhash = client
            .svm_get_latest_blockhash()
            .await
            .map_err(|e| anyhow!("Failed to retrieve the latest blockhash: {e}"))?;
    
        let rent_balance = client
            .get_minimum_balance_for_rent_exemption(Mint::LEN)
            .await
            .map_err(|e| anyhow!("Failed to get a rent exempt balance: {e}"))?;
    
        let create_mint_account = solana_sdk::system_instruction::create_account(
            &payer.pubkey(),
            &mint_account.pubkey(),
            rent_balance,
            Mint::LEN as u64,
            &spl_token::id(),
        );
    
        let initialize_mint = spl_token::instruction::initialize_mint(
            &spl_token::id(),
            &mint_account.pubkey(),
            &payer.pubkey(), // mint authority
            None,
            0,
        )
        .map_err(|e| anyhow!("Failed to create initialize mint instruction: {e}"))?;
    
        let create_mint_tx = Transaction::new_signed_with_payer(
            &[create_mint_account, initialize_mint],
            Some(&payer.pubkey()),
            &[payer, &mint_account],
            latest_blockhash,
        );
    
        let tx_hash = client
            .svm_send_transaction(&create_mint_tx)
            .await
            .map_err(|e| anyhow!("Failed to send a transaction: {e}"))?
            .to_string();
    
        sleep(Duration::from_secs(SLEEP_DURATION_SECONDS)).await;
    
        let mint_acc_info = client
            .svm_get_account_info(mint_account.pubkey())
            .await
            .map_err(|e| anyhow!("Mint account should exist after transaction: {e}"))?;
    
        // Deserialize the mint account data
        let mint_data = Mint::unpack(&mint_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack mint account data: {e}"))?;
    
        Ok(mint_account)
    }
  • Create Associated Token Account

    pub(crate) async fn create_associated_token_account(
        client: &TestClient,
        payer: &Keypair,
        target_account_pubkey: &Pubkey,
        mint_account_pubkey: &Pubkey,
    ) -> anyhow::Result<()> {
        let latest_blockhash = client
            .svm_get_latest_blockhash()
            .await
            .map_err(|e| anyhow!("Failed to retrieve the latest blockhash: {e}"))?;
    
        let create_ata = spl_associated_token_account::instruction::create_associated_token_account(
            &payer.pubkey(),
            target_account_pubkey,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let create_ata_tx = Transaction::new_signed_with_payer(
            &[create_ata],
            Some(&payer.pubkey()),
            &[&payer],
            latest_blockhash,
        );
    
        let tx_hash = client
            .svm_send_transaction(&create_ata_tx)
            .await
            .map_err(|e| anyhow!("Failed to send a transaction: {e}"))?
            .to_string();
    
        sleep(Duration::from_secs(SLEEP_DURATION_SECONDS)).await;
    
        let ata_pubkey = get_associated_token_address_with_program_id(
            target_account_pubkey,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let ata_acc_info = client.svm_get_account_info(ata_pubkey).await.map_err(|e| {
            anyhow!("Failed to retrieve associated token account after transaction: {e}")
        })?;
    
        Ok(())
    }
  • Mint Tokens

    pub(crate) async fn transfer_tokens(
        client: &TestClient,
        payer: &Keypair,
        sender: &Keypair,
        receiver_account_pubkey: &Pubkey,
        mint_account_pubkey: &Pubkey,
        amount: u64,
    ) -> anyhow::Result<()> {
        let sender_key = sender.pubkey();
        let latest_blockhash = client
            .svm_get_latest_blockhash()
            .await
            .map_err(|e| anyhow!("Failed to retrieve the latest blockhash: {e}"))?;
    
        let sender_ata_pubkey = get_associated_token_address_with_program_id(
            &sender_key,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let receiver_ata_pubkey = get_associated_token_address_with_program_id(
            receiver_account_pubkey,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let sender_ata_acc_info = client
            .svm_get_account_info(sender_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {sender_ata_pubkey}: {e}"))?;
        let sender_token_account_data = TokenAccount::unpack(&sender_ata_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack sender's token account data: {e}"))?;
    
        let receiver_ata_acc_info = client
            .svm_get_account_info(receiver_ata_pubkey)
            .await
            .map_err(|e| anyhow!("failed to retrieve account {receiver_ata_pubkey}: {e}"))?;
        let receiver_token_account_data = TokenAccount::unpack(&receiver_ata_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack receiver's token account data: {e}"))?;
    
        // Get initial balances for tests
        let initial_sender_tokens = sender_token_account_data.amount;
        let initial_receiver_tokens = receiver_token_account_data.amount;
    
        let transfer = spl_token::instruction::transfer_checked(
            &spl_token::id(),
            &sender_ata_pubkey,
            mint_account_pubkey,
            &receiver_ata_pubkey,
            &sender_key,
            &[],
            amount,
            0,
        )
        .map_err(|e| anyhow!("Failed to create transfer checked instruction: {e}"))?;
    
        let transfer_tx = Transaction::new_signed_with_payer(
            &[transfer],
            Some(&payer.pubkey()),
            &[&payer, &sender],
            latest_blockhash,
        );
    
        let tx_hash = client
            .svm_send_transaction(&transfer_tx)
            .await
            .map_err(|e| anyhow!("Failed to send a transaction: {e}"))?
            .to_string();
    
        sleep(Duration::from_secs(SLEEP_DURATION_SECONDS)).await;
    
        let sender_acc_info = client
            .svm_get_account_info(sender_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {sender_ata_pubkey}: {e}"))?;
        let sender_token_account_data = TokenAccount::unpack(&sender_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack sender's token account data: {e}"))?;
    
        let receiver_acc_info = client
            .svm_get_account_info(receiver_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {receiver_ata_pubkey}: {e}"))?;
        let receiver_token_account_data = TokenAccount::unpack(&receiver_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack receiver's token account data: {e}"))?;
    
        Ok(())
    }
  • Transfer Tokens

    pub(crate) async fn transfer_tokens(
        client: &TestClient,
        payer: &Keypair,
        sender: &Keypair,
        receiver_account_pubkey: &Pubkey,
        mint_account_pubkey: &Pubkey,
        amount: u64,
    ) -> anyhow::Result<()> {
        let sender_key = sender.pubkey();
        let latest_blockhash = client
            .svm_get_latest_blockhash()
            .await
            .map_err(|e| anyhow!("Failed to retrieve the latest blockhash: {e}"))?;
    
        let sender_ata_pubkey = get_associated_token_address_with_program_id(
            &sender_key,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let receiver_ata_pubkey = get_associated_token_address_with_program_id(
            receiver_account_pubkey,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let sender_ata_acc_info = client
            .svm_get_account_info(sender_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {sender_ata_pubkey}: {e}"))?;
        let sender_token_account_data = TokenAccount::unpack(&sender_ata_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack sender's token account data: {e}"))?;
        debug!("Sender's token account data:: {sender_token_account_data:?}");
    
        let receiver_ata_acc_info = client
            .svm_get_account_info(receiver_ata_pubkey)
            .await
            .map_err(|e| anyhow!("failed to retrieve account {receiver_ata_pubkey}: {e}"))?;
        let receiver_token_account_data = TokenAccount::unpack(&receiver_ata_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack receiver's token account data: {e}"))?;
        debug!("Receiver's token account data: {receiver_token_account_data:?}");
    
        // Get initial balances for tests
        let initial_sender_tokens = sender_token_account_data.amount;
        let initial_receiver_tokens = receiver_token_account_data.amount;
    
        let transfer = spl_token::instruction::transfer_checked(
            &spl_token::id(),
            &sender_ata_pubkey,
            mint_account_pubkey,
            &receiver_ata_pubkey,
            &sender_key,
            &[],
            amount,
            0,
        )
        .map_err(|e| anyhow!("Failed to create transfer checked instruction: {e}"))?;
    
        let transfer_tx = Transaction::new_signed_with_payer(
            &[transfer],
            Some(&payer.pubkey()),
            &[&payer, &sender],
            latest_blockhash,
        );
    
        let tx_hash = client
            .svm_send_transaction(&transfer_tx)
            .await
            .map_err(|e| anyhow!("Failed to send a transaction: {e}"))?
            .to_string();
    
        sleep(Duration::from_secs(SLEEP_DURATION_SECONDS)).await;
    
        let sender_acc_info = client
            .svm_get_account_info(sender_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {sender_ata_pubkey}: {e}"))?;
        let sender_token_account_data = TokenAccount::unpack(&sender_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack sender's token account data: {e}"))?;
    
        let receiver_acc_info = client
            .svm_get_account_info(receiver_ata_pubkey)
            .await
            .map_err(|e| anyhow!("Failed to retrieve account {receiver_ata_pubkey}: {e}"))?;
        let receiver_token_account_data = TokenAccount::unpack(&receiver_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack receiver's token account data: {e}"))?;
    
        Ok(())
    }
  • Burn Tokens

    pub(crate) async fn burn_tokens(
        client: &TestClient,
        payer: &Keypair,
        target_account: &Keypair,
        mint_account_pubkey: &Pubkey,
        amount: u64,
    ) -> anyhow::Result<()> {
        let target_key = target_account.pubkey();
        let latest_blockhash = client
            .svm_get_latest_blockhash()
            .await
            .map_err(|e| anyhow!("Failed to retrieve the latest blockhash: {e}"))?;
    
        let target_ata_pubkey = get_associated_token_address_with_program_id(
            &target_key,
            mint_account_pubkey,
            &spl_token::id(),
        );
    
        let target_acc_info = client
            .svm_get_account_info(target_ata_pubkey)
            .await
            .map_err(|e| anyhow!("failed to retrieve account {target_ata_pubkey}: {e}"))?;
        let target_token_account_data = TokenAccount::unpack(&target_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack token account data: {e}"))?;
        debug!("Target's token account data:: {target_token_account_data:?}");
    
        let initial_target_tokens = target_token_account_data.amount;
    
        let burn = spl_token::instruction::burn_checked(
            &spl_token::id(),
            &target_ata_pubkey,
            mint_account_pubkey,
            &target_key,
            &[],
            amount,
            0,
        )
        .map_err(|e| anyhow!("Failed to create burn tx: {e}"))?;
    
        let burn_tx = Transaction::new_signed_with_payer(
            &[burn],
            Some(&payer.pubkey()),
            &[&payer, &target_account],
            latest_blockhash,
        );
    
        let tx_hash = client
            .svm_send_transaction(&burn_tx)
            .await
            .map_err(|e| anyhow!("Failed to send a transaction: {e}"))?
            .to_string();
    
        sleep(Duration::from_secs(SLEEP_DURATION_SECONDS)).await;
    
        let target_acc_info = client
            .svm_get_account_info(target_ata_pubkey)
            .await
            .map_err(|e| anyhow!("failed to retrieve account {target_ata_pubkey}: {e}"))?;
        let target_token_account_data = TokenAccount::unpack(&target_acc_info.data)
            .map_err(|e| anyhow!("Failed to unpack token account data: {e}"))?;
    
        Ok(())
    }

Last updated

Was this helpful?