Quantcast
Channel: Recent Questions - Solana Stack Exchange
Viewing all articles
Browse latest Browse all 7894

Failure in transferring SPL tokens from one account to another inside Solana Program?

$
0
0

Here's my Solana Program. It defines the logic for staking SPL tokens.

use anchor_lang::prelude::*;use anchor_spl::{    token::{TokenAccount, Mint,Token, Transfer, transfer, Approve},    associated_token::{AssociatedToken, create, get_associated_token_address}};use std::mem::size_of;// use solana_program::clock::Clock;declare_id!("8eeWAxSis5uy5ox3ucNj2DouFsc7bfMvTnGx3wc1GaXG");pub mod constants {    pub const VAULT_SEED: &[u8] = b"vault";    pub const STAKE_INFO_ACCOUNT: &[u8] = b"stake_info_account";    pub const TOKEN_SEED: &[u8] = b"token_seed";}#[program]pub mod staking_solana {    use super::*;    pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {        Ok(())    }    pub fn stake(ctx: Context<Stake>, amt:u64) -> Result<()> {        let stake_info: &mut Account<'_, StakeInfo> = &mut ctx.accounts.stake_info_account;         if (stake_info.is_staked) {            return Err(ErrorCodes::IsStaked.into());        }        if (amt == 0) {            return Err(ErrorCodes::NoTokens.into());        }        // staking logic        let clock  = &mut ctx.accounts.clock;        stake_info.stake_at_slot = clock.slot;        stake_info.is_staked = true;        // format the staking amount        // 10^decimals * amt        let stake_amt = amt.checked_mul(10u64.pow(ctx.accounts.mint.decimals as u32)).unwrap();       msg!("Transferring {} tokens from user_token_account ({}) to user_staking_account ({})",             stake_amt,             ctx.accounts.user_token_account.key(),             ctx.accounts.user_staking_ata.key()         );        // transfer stake amt to user_Stake_account from user_token_account        anchor_spl::token::transfer(            CpiContext::new(                ctx.accounts.token_program.to_account_info(),                Transfer {                    from: ctx.accounts.user_token_account.to_account_info(),                    to: ctx.accounts.user_staking_ata.to_account_info(),                    authority: ctx.accounts.signer.to_account_info(),                }            ),            stake_amt         )?;        msg!("balance of the user token account is {:?}", ctx.accounts.user_token_account.amount);        msg!("balance of the user stake account is {:?}", ctx.accounts.user_staking_ata.amount);        Ok(())    }    pub fn destake(ctx: Context<Initialize>) -> Result<()> {        Ok(())    }}#[derive(Accounts)]pub struct Initialize<'info> {    #[account(mut)]    pub signer : Signer<'info>,    #[account(        init_if_needed,        seeds = [constants::VAULT_SEED],        bump,        payer = signer,        token::mint = mint,        token::authority = token_vault_account,    )]    pub token_vault_account : Account<'info, TokenAccount>,    #[account()]    pub mint: Account<'info, Mint>,    pub token_program: Program<'info, Token>,    pub system_program: Program<'info, System>,}#[derive(Accounts)]pub struct Stake<'info> {    #[account(mut)]    pub signer : Signer<'info>,    pub clock : Sysvar<'info, Clock>,    #[account(        init_if_needed,        seeds = [constants::STAKE_INFO_ACCOUNT, signer.key.as_ref()],        space = size_of::<StakeInfo>() + 8,        bump,        payer = signer,    )]    pub stake_info_account : Account<'info, StakeInfo>,    #[account(        init_if_needed,        seeds = [constants::TOKEN_SEED, signer.key.as_ref()],        bump,        payer = signer,        token::mint = mint,        token::authority = user_staking_account    )]    pub user_staking_account : Account<'info, TokenAccount>,    #[account(        init_if_needed,        payer = signer,        associated_token::mint = mint,        associated_token::authority = user_staking_account    )]    user_staking_ata : Account<'info, TokenAccount>,    #[account(        mut,        associated_token::mint = mint,        associated_token::authority = signer    )]    pub user_token_account : Account<'info, TokenAccount>,    #[account()]    pub mint: Account<'info, Mint>,    pub token_program: Program<'info, Token>,    pub associated_token_program : Program<'info, AssociatedToken>,    pub system_program: Program<'info, System>,}#[account]pub struct StakeInfo {    pub total_staked: u64,    pub remaining_rewards: u64,    pub stake_at_slot: u64,    pub is_staked: bool,}#[error_code]pub enum ErrorCodes{    #[msg("Tokens already staked")]    IsStaked,    #[msg("Tokens not staked")]    NotStaked,    #[msg("No Tokens To Stake")]    NoTokens}

There are 5 different accounts involved illustrated below

5 Accounts Involved

The SPL tokens are supposed to be sent from User's Token Account to User STaking Info Acc.

Here's the TS test suite for this

  it("stake", async () => {    // Create user account    const local_connection = new Connection("http://localhost:8899", {      commitment: "confirmed",    });    console.log("mint is:", mint);    const user_addr = Keypair.generate();    // creating ATA to the user (user is the owner of token account)    const user = await getOrCreateAssociatedTokenAccount(      local_connection,      payer.payer,      mint,      payer.publicKey // Correct owner of token account is the user    );    console.log("user token account is ", user.address);    // Mint some tokens to the user (e.g., 100 tokens)    await mintTo(      local_connection,      payer.payer,      mint,      user.address, // Correct user token account      payer.publicKey, // payer is the owner of the mint      1e11, // This mints 100 tokens (1e11 atomic units with 9 decimals)      [payer.payer]    );    // Get the user's initial token balance before staking    const initialUserBalance = await getTokenBalance(      local_connection,      mint,      payer.publicKey // Use the correct user token account    );    console.log("Initial User Token Balance:", initialUserBalance);    // Get the stake info account for the program    const [stake_info] = PublicKey.findProgramAddressSync(      [Buffer.from("stake_info_account"), payer.publicKey.toBuffer()],      program.programId    );    // Get the user_stake PDA (program-derived address)    const [stake_account] = PublicKey.findProgramAddressSync(      [Buffer.from("token_seed"), payer.publicKey.toBuffer()], // Ensure the correct user address is used for PDA derivation      program.programId    );    // Create an associated token account for the user_stake PDA (program owns the stake account)    const stake_acc_ata = await getOrCreateAssociatedTokenAccount(      local_connection,      payer.payer,      mint,      stake_account, // Stake account owned by the program's PDA      true // This ensures it creates the ATA for the program's PDA    );    console.log("stake ATA is ", stake_acc_ata.address);    console.log("user token account is ", user.address);    // Staking 1 token (1e9 in atomic units, considering 9 decimals)    const amountToStake = new anchor.BN(1); // This represents 1 token    // Execute the staking transaction    const tx = await program.methods      .stake(amountToStake) // Assuming 1 token is being staked      .signers([payer.payer])      .accountsPartial({        stakeInfoAccount: stake_info,        userStakingAccount: stake_account,        userTokenAccount: user.address, // Use the correct user token account        mint: mint,        signer: payer.publicKey, // The signer should be the user staking the tokens      })      .rpc();    console.log("Transaction signature:", tx);    // Get the user's token balance after staking    const token_amt = (await getAccount(local_connection, user.address)).amount;    console.log("Final User Token Balance:", token_amt);    // Check that the user's balance was reduced by the staked amount (1 token)    assert.equal(      BigInt(initialUserBalance) - token_amt,      BigInt(1e9), // 1 token in atomic units (with 9 decimals)"User's token balance should decrease by 1 token"    );    // Get the staked account balance to verify the staked amount    const stakedAccountBalance = await getTokenBalance(      local_connection,      mint,      stake_acc_ata.address // Check the stake account's balance    );    console.log("Staked Account Balance:", stakedAccountBalance);    // Check that the staked account has the staked tokens (1 token)    assert.equal(      BigInt(stakedAccountBalance),      BigInt(1e9), // 1 token in atomic units (with 9 decimals)"Staked account should have the correct token amount (1 token)"    );  });

There's NO issue in the function execution; just that after the tx when we check the balance of the user's staking info account's Token account; it returns a balance of 0.

I am a newbie in Solana dev, so forgive me for asking something that may be obvious!!


Viewing all articles
Browse latest Browse all 7894

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>