0

I've been working on Solana program that should hold two different tokens for me in a Vault in an ATA. The first token should be deposited during Vault initialisation, the second will be deposited by exchanging for the first, will some fixed ratio. Both ATAs for the PDA of the Vault i wanted to be created during Vault initialisation. The problem is i keep getting error:

Error initializing vault: Error: AnchorError caused by account: heim_mint. Error Code: AccountOwnedByWrongProgram. Error Number: 3007. Error Message: The given account is owned by a different program than expected.
Program log: Left:
Program log: NativeLoader1111111111111111111111111111111
Program log: Right:
Program log: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

Which is weird, because this mint is owned by a TokenProgram (https://solscan.io/token/BVqRM5tbqerXFnGBSDZMvGEvdUx3aVNUadBiQBbjAjxB?cluster=devnet)

This is my Anchor Rust code:

#[program]
pub mod heim_token {
    use super::*;

    pub fn initialize_vault(ctx: Context<InitializeVault>, initial_heim_amount: u64) -> Result<()> {
        if ctx.accounts.authority_heim_ata.mint != mints::HEIM_ADDRESS {
            return Err(ProgramError::InvalidAccountData.into());
        }
        let vault = &mut ctx.accounts.vault;
        vault.authority = *ctx.accounts.authority.key;
        vault.heim_amount = initial_heim_amount;
        vault.usdc_amount = 0;

        let cpi_accounts = SplTransfer {
            from: ctx.accounts.authority_heim_ata.to_account_info().clone(),
            to: ctx.accounts.vault_heim_ata.to_account_info().clone(),
            authority: ctx.accounts.authority.to_account_info().clone()
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();

        token::transfer(CpiContext::new(cpi_program, cpi_accounts), initial_heim_amount)?;

        Ok(())
    }
}

#[account]
pub struct Vault {
    pub authority: Pubkey,
    pub heim_amount: u64,
    pub usdc_amount: u64,
    pub bump: u8,
}

#[derive(Accounts)]
pub struct InitializeVault<'info> {
    #[account(mut, signer)]
    pub authority: Signer<'info>,
    #[account(
        init,
        payer = authority,
        space = 8 + 64 + 32 + 1,
        seeds=[b"heim_vault".as_ref()],
        bump
    )]
    pub vault: Account<'info, Vault>,
    #[account(
        init,
        payer = authority,
        associated_token::mint = heim_mint,
        associated_token::authority = authority,
    )]
    pub vault_heim_ata: Account<'info, TokenAccount>,
    pub heim_mint: Account<'info, Mint>,
    #[account(
        init,
        payer = authority,
        associated_token::mint = usdc_mint,
        associated_token::authority = authority,
    )]
    pub vault_usdc_ata: Account<'info, TokenAccount>,
    pub usdc_mint: Account<'info, Mint>,
    #[account(mut)]
    pub authority_heim_ata: Account<'info, TokenAccount>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub rent: Sysvar<'info, Rent>, // do i really need it here? google it
    pub associated_token_program: Program<'info, AssociatedToken>,
}

And this is JS client code:

  const getProvider = () => {
    const connection = new Connection(network, opts.preflightCommitment)
    return new AnchorProvider(connection, window.solana, opts.preflightCommitment)
  }

  const initializeVault = async() => {
    try {
      const provider = getProvider()
      const program = new Program(idl, programID, provider)

      const heimMint = new PublicKey(
        "BVqRM5tbqerXFnGBSDZMvGEvdUx3aVNUadBiQBbjAjxB"
      );
      const usdcMint = new PublicKey(
        "NkHsgx5xBCPp6Gi33KTPgD2weVJJY884NiuVjz76APn"
      );
      const mintInfo = await provider.connection.getAccountInfo(heimMint)
      const data = MintLayout.decode(mintInfo.data);
      const senderATA = await getAssociatedTokenAddress(heimMint, provider.wallet.publicKey);

      const heim_amount = new BN(1000).mul(new BN(10).pow(new BN(data.decimals)))
      const result = await program.methods.initializeVault(heim_amount).accounts(
        {
          user: provider.wallet.publicKey,
          systemProgram: SystemProgram.programId,
          authorityHeimAta: senderATA,
          tokenProgram: TOKEN_PROGRAM_ID,
          heimMint: heimMint,
          usdcMint: usdcMint,
          associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
        }
      ).rpc()
      console.log("result", result)

      getVault();
    } catch (error) {
      console.error('Error initializing vault:', error)
    }
  }

Could you please help me with this? Everything seems to be fine, everything i find in the documentation looks like what i did, i tried searching similar cases but i can't see anything that would help me, ChatGPT is saying the code is fine. But something isn't. Thanks in advance.

3
  • could you send a link to solscan with a failed transaction please ? also, are you sure about your usdc mint address ? Usually it's EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v ?
    – Effe2
    Nov 9, 2023 at 8:56
  • it's my "mock" usdc, i minted it on devnet and use it for testing. I will use real usdc address on mainnet. This is the wallet i'm working with: solscan.io/account/… But it looks like there are not transactions for initialize_vault Nov 9, 2023 at 9:16
  • try to run your tx with skipPreflight = true, it should be sent to the blockchain, then i will be able to check account addresses Edit: see neft's answer below, great point
    – Effe2
    Nov 9, 2023 at 14:47

1 Answer 1

1

Your validation struct expects the following accounts:

authority
vault
vault_heim_ata
heim_mint
vault_usdc_ata
usdc_mint
authority_heim_ata
system_program
token_program
rent
associated_token_program

In your JS code you are providing something different:

user
systemProgram 
authorityHeimAta
tokenProgram
heimMint
usdcMint
associatedTokenProgram

For example - the field "user" doesn't exist on the validation struct at all.

Your IDE should throw some warnings at you. If it does not, make sure you're using the latest IDL for your program - the IDL is generated automatically when you run anchor build

The error message is confusing, but since you're not sending all the data that the entrypoint expects, you will find that the fields get scrambled.

In this case, whichever account ends up getting passed as the fourth one (the one that should be heim_mint), happens to be owned by NativeLoader1111111111111111111111111111111. Could be system program for example.

If you have preflight checks switched off, you can also look at the transaction on solana explorer - that will show you exactly which account gets passed and in which position (see the screenshot for a sample tx).

enter image description here

2
  • 1
    Thanks @neft, it seems i also need to use anchor.web3.PublicKey.findProgramAddress to pass Vault address i'm creating - i thought Anchor will do it for me. Nov 9, 2023 at 20:14
  • If the answer looks ok, please accept. It makes the correct answer easier to index and find... and I get brownie points :D
    – neft
    Nov 9, 2023 at 23:05

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.