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.