1

I am creating an ICO contract, where first i initialize the contract and then i am going to deposit some SPL token that i create into it.

I have currently 2 test, one where i initialize the contract and then second test to deposit my newly created SPL token into that contract. I am having trouble with depositing the SPL token into that contract, as I get an error saying

AnchorError caused by account: from_token_account. Error Code: AccountNotInitialized. Error Number: 3012. Error Message: The program expected this account to be already initialized.

In my test i actually initialize the account, as such.

   // lets create a token account for the program
    const programTokenAccount = await getOrCreateAssociatedTokenAccount(
      connectionToSol, 
      tokenProvider,
      tokenMint,
      programDeployer.publicKey 
    );

I am not really sure where the issue is, here is the whole contract.

    #![allow(clippy::result_large_err)]
    
    use anchor_lang::prelude::*;
    use anchor_spl::token::{self, Transfer, TokenAccount, Token};
    
    declare_id!("");
    
    #[program]
    pub mod swan {
        use super::*;
    
        pub fn init(ctx: Context<Initialize>, token_provider: Pubkey, beneficiary: Pubkey, safeguarding_account: Pubkey, raise_cap: u64) -> Result<()> {
          let state = &mut ctx.accounts.state;
          //require!(!state.initialized, CustomError::AlreadyInitialized);
    
          state.token_provider = token_provider;
          state.beneficiary = beneficiary;
          state.safeguarding_account = safeguarding_account;
          state.initialized = true;
          state.raise_cap = raise_cap;
          
          Ok(())
      }
    
      // Deposit tokens to the ICO contract.
        // Can be called only once by the token provider
        pub fn deposit(ctx: Context<Deposit>, amount: u64, token_mint: Pubkey) -> Result<()> {
          let state = &mut ctx.accounts.state;
          require!(ctx.accounts.token_authority.key() == state.token_provider, CustomError::UnauthorizedCaller);
          require!(state.total_tokens == 0, CustomError::TokensAlreadyDeposited);
          require!(ctx.accounts.from_token_account.mint == token_mint, CustomError::InvalidTokenMint);
    
          let cpi_accounts = Transfer {
              from: ctx.accounts.from_token_account.to_account_info(),
              to: ctx.accounts.program_token_account.to_account_info(),
              authority: ctx.accounts.token_authority.to_account_info(),
          };
          let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
          token::transfer(cpi_ctx, amount)?;
    
          state.token_mint = token_mint;
          state.total_tokens = amount;
    
          Ok(())
      }
    }
    
    #[derive(Accounts)]
    pub struct Initialize<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
        #[account(
          init,
          payer = signer,
          space = 8 + State::INIT_SPACE,
        )]
        pub state: Account<'info, State>,
        pub system_program: Program<'info, System>,
    }
    
    #[account]
    #[derive(InitSpace)]
    pub struct State {
        pub initialized: bool,
        pub participation_active: bool,
        pub participation_end: u64,
        pub raise_cap: u64,
        pub total_contributed: u64,
        pub total_tokens: u64,
        pub tokens_distributed: bool,
        pub recipient_lamports: u64,
        pub token_provider: Pubkey,
        pub beneficiary: Pubkey,
        pub safeguarding_account: Pubkey,
        pub token_mint: Pubkey,
        pub total_cancelled: u64,  // Track total amount of SOL to be refunded
        pub large_investor_count: u64,  // Track number of investors with >= 100 SOL
        pub unique_investor_count: u64,  // Track number of first-time investors
        pub active_early_investor_count: u64,  // Track number of non-cancelled early investors
    }
    
    #[derive(Accounts)]
    pub struct Deposit<'info> {
        #[account(mut)]
        pub state: Account<'info, State>,
        pub token_authority: Signer<'info>,
        #[account(
            mut,
        )]
        pub from_token_account: Account<'info, TokenAccount>,
        #[account(
            mut,
        )]
        pub program_token_account: Account<'info, TokenAccount>,
        pub token_program: Program<'info, Token>,
    }
    
    #[error_code]
    pub enum CustomError {
        #[msg("Unauthorized caller.")]
        UnauthorizedCaller,
        #[msg("Tokens have already been deposited.")]
        TokensAlreadyDeposited,
        #[msg("Invalid token mint.")]
        InvalidTokenMint,
    }

And below is the test I am running where the cause of the issue is.

    import * as anchor from "@coral-xyz/anchor";
    import { Program } from "@coral-xyz/anchor";
    import {
      clusterApiUrl,
      Connection,
      Keypair,
      LAMPORTS_PER_SOL,
      PublicKey,
    } from "@solana/web3.js";
    import { Swan } from "../target/types/swan";
    import { BankrunProvider, startAnchor } from "anchor-bankrun";
    import {
      createAccount,
      createMint,
      getOrCreateAssociatedTokenAccount,
      mintTo,
      TOKEN_PROGRAM_ID,
    } from "@solana/spl-token";
    
    const IDL = require("../target/idl/swan.json");
    
    const swanAddress = new PublicKey(
      "AsjZ3kWAUSQRNt2pZVeJkywhZ6gpLpHZmJjduPmKZDZZ"
    );
    
    describe("swan", () => {
      // Configure the client to use the local cluster.
      let context: any;
      let provider: BankrunProvider;
      let swanProgram: Program<Swan>;
      let state: Keypair;
      let fromTokenAccount: PublicKey;
      let mintedCoinAssociatedAddress: PublicKey;
      let tokenMint: PublicKey;
      let tokenAccount: any;
      const programDeployer = Keypair.generate();
      const tokenProvider: Keypair = Keypair.generate();
      const Beneficiary: Keypair = Keypair.generate();
      const safeGuard: Keypair = Keypair.generate();
      const connectionToSol = new Connection("http://localhost:8899", "confirmed");
      //const connectionToSol = new Connection(clusterApiUrl("devnet"));
    
      beforeAll(async () => {
        context = await startAnchor(
          "",
          [{ name: "swan", programId: swanAddress }],
          []
        );
        provider = new BankrunProvider(context);
        console.log(provider.connection);
        swanProgram = new Program<Swan>(IDL, provider);
    
        await swanProgram.methods
          .init(
            programDeployer.publicKey,
            Beneficiary.publicKey,
            safeGuard.publicKey,
            new anchor.BN(1000000000)
          )
          .accounts({
            state: programDeployer.publicKey, // Pass the new keypair for the state account
          })
          .signers([programDeployer])
          .rpc();
    
        // Create a new SPL Token Mint
        const airdropSignature = await connectionToSol.requestAirdrop(
          tokenProvider.publicKey,
          LAMPORTS_PER_SOL // Adjust based on the expected fees
        );
        await connectionToSol.confirmTransaction(airdropSignature);
    
        const balance = await connectionToSol.getBalance(tokenProvider.publicKey);
        console.log("Token Provider Balance:", balance);
        if (balance === 0) {
          console.error("Token Provider has no SOL!");
        }
    
        tokenMint = await createMint(
          connectionToSol,
          tokenProvider,
          tokenProvider.publicKey,
          tokenProvider.publicKey,
          9 // Match CLI decimals
        );
    
        tokenAccount = await getOrCreateAssociatedTokenAccount(
          connectionToSol,
          tokenProvider,
          tokenMint,
          tokenProvider.publicKey
        );
        mintedCoinAssociatedAddress = tokenAccount.address;
    
        // Mint tokens to `fromTokenAccount`
        await mintTo(
          connectionToSol,
          tokenProvider,
          tokenMint,
          mintedCoinAssociatedAddress,
          tokenProvider,
          100000000000 // Amount to mint (e.g., 1 billion tokens)
        );
      });
    
      it("Initialize Swan", async () => {
        const stateAccount = await swanProgram.account.state.fetch(
          programDeployer.publicKey
        );
        expect(stateAccount.initialized).toEqual(true);
      });
    
      it("Deposit", async () => {
        // lets create a token account for the program
        const programTokenAccount = await getOrCreateAssociatedTokenAccount(
          connectionToSol, // Connection to Solana
          tokenProvider, // Token provider who will pay for the transaction
          tokenMint, // The mint of the token
          programDeployer.publicKey // The owner of the program's token account (programDeployer)
        );
    
        await swanProgram.methods
          .deposit(
            new anchor.BN(10000000000), // amount
            tokenMint // tokemint
          )
          .accounts({
            state: programDeployer.publicKey,
            tokenAuthority: tokenProvider.publicKey,
            fromTokenAccount: mintedCoinAssociatedAddress,
            programTokenAccount: programTokenAccount.address,
          })
          .signers([tokenProvider])
          .rpc();
        // // Verify the deposit
        // const programTokenAccountInfo = await getAccount(provider.connection, programTokenAccount);
        // const stateAccountInfo = await program.account.state.fetch(state.publicKey);
    
        // expect(programTokenAccountInfo.amount.toString()).toEqual("1000000000");
        // expect(stateAccountInfo.totalTokens.toString()).toEqual("1000000000");
        // expect(stateAccountInfo.tokenMint.toBase58()).toEqual(tokenMint.toBase58());
      });
    });

So the function

await swanProgram.methods
      .deposit(

fails, when the test is ran with anchor test everything else seems to work fine. Any help would be appreciated, i spent few days on this.

1 Answer 1

0

The error suggest that you are calling the deposit instruction with the from_token_account token account not initialized.

In your case you are calling the createMint, getOrCreateAssociatedTokenAccount and mintTo instructions one after another.

Perhaps try sleeping between each of those calls and verify that the token account is indeed created.

let tokenMint = await createMint(
 // -- snip
);

sleep(10000);

let tokenAccount = await getOrCreateAssociatedTokenAccount(
 // -- snip
);
let mintedCoinAssociatedAddress = tokenAccount.address;

sleep(5000);

await mintTo(
 // -- snip
);
  
4
  • Added the delay, same error. Commented Nov 28 at 14:26
  • that's strange. Can you change your connection to point to devnet and try again.
    – Jimii
    Commented Nov 28 at 14:57
  • Yea same issue, i am guessing the way i wrote the lib.rs code maybe wrong? Or it could be that anchor is not able to figure out the wallets? I usually run the test as anchor test --ski-deploy Commented Nov 28 at 15:05
  • So i checked, that both accounts are initialized, isInitialized: true, so its a bit confusing why isnt the code accepting that. Commented Nov 28 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.