0

Hey guys I'm developing a dApp on Solana and it was working fine before I recently edited my Rust program and redeployed. I added some functions and added a property to one of my structs, but other than that, I didn't touch anything else. However, now I keep getting this error when I try to fetch my program account to render one of it's objects:

Error in getPosts: RangeError: Trying to access beyond buffer length
    at checkOffset (index.js:1127:1)
    at Uint8Array.readUIntLE (index.js:1134:1)
    at UInt.decode (Layout.js:570:1)
    at Structure.decode (Layout.js:1234:1)
    at WrappedLayout.decode (index.ts:102:1)
    at WrappedLayout.decode (index.ts:102:1)
    at Structure.decode (Layout.js:1234:1)
    at Sequence.decode (Layout.js:1090:1)
    at Structure.decode (Layout.js:1234:1)

This is the function that is catching the error:

  const getPosts = async() => {
    try {
      const provider = getProvider();
      console.log("Got the provider", provider);
      const program = new Program(idl, programID, provider);
      console.log("Got the program", program);
      console.log("BaseAccount",program.account.baseAccount);
      const account = await program.account.baseAccount.fetch(baseAccount.publicKey);

      console.log("Got the account:", account)
      setPosts(account.postList);
    } catch (error) {
      console.log("Error in getPosts:", error)
      setPosts(null);
    }
  }

I logged the provider, program, and baseAccount to the console to check if those were causing the error but all of them appear to be intact, the exact line that is causing the error I think is the following because my account is not logging:

const account = await program.account.baseAccount.fetch(baseAccount.publicKey);

I already tried creating a new keypair for my program account and searching for a solution to this buffer length error but to no avail. Does anyone know what might be a possible cause of this problem? I've redeployed before and this hasn't caused an issue, I'm not sure if I messed up my program account when redeploying or something. If you have any questions or request any extra information I'm happy to give it to you .

Here is some relevant context:

//Function that initializes count (lib.rs)
pub fn start_stuff_off(ctx: Context<StartStuffOff>) -> Result<()> {
        let base_account = &mut ctx.accounts.base_account;
        base_account.total_posts = 0;
        Ok(())
    }

//RPC to previous function (in App.js):
 const createPostAccount = async () => {
    try {
      const provider = getProvider();
      const program = new Program(idl, programID, provider);
      console.log("ping");
      await program.methods.startStuffOff()
        .accounts({
          baseAccount: baseAccount.publicKey,
          user: provider.wallet.publicKey,
          systemProgram: SystemProgram.programId,
        })
        .signers([baseAccount])
        .rpc();
      console.log("Created a new BaseAccount w/address:", baseAccount.publicKey);
      await getPosts();
  
    } catch (error) {
      console.log("Error creating BaseAccount account:", error);
    }
  }

//Initializing Struct
#[derive(Accounts)]
pub struct StartStuffOff<'info> {
    #[account(init, payer = user, space = 10000)] //Change space? //originally 10k
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

//Initializing BaseAccount
const arr = Object.values(kp._keypair.secretKey);
const secret = new Uint8Array(arr);
const baseAccount = web3.Keypair.fromSecretKey(secret);

//Getting our program ID
const programID = new PublicKey(idl.metadata.address);

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

//New macro I added for redeploy
#[derive(Accounts)]
pub struct Upvote<'info> {
    #[account(mut)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
}

//New macro I added for redeploy
#[derive(Accounts)]
pub struct Downvote<'info> {
    #[account(mut)]
    pub base_account: Account<'info, BaseAccount>,
    #[account(mut)]
    pub user: Signer<'info>,
}

#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct PostStruct {
    pub post_title: String,
    pub post_intro: String,
    pub post_body: String,
    pub post_conclusion: String,
    pub user_address: Pubkey,
    pub points: u64, //New line
}

#[account]
pub struct BaseAccount {
    pub total_posts: u64,
    pub post_list: Vec<PostStruct>,
}

//New function I added for redeploy
pub fn upvote(ctx: Context<Upvote>, index: i64) -> Result<()> { 
        let base_account = &mut ctx.accounts.base_account;
        let user = &mut ctx.accounts.user;
        let index_as_usize = index as usize;

        base_account.post_list[index_as_usize].points += 1;
        Ok(())
    }
//New function I added for redeploy
    pub fn downvote(ctx: Context<Downvote>, index: i64) -> Result<()> { 
        let base_account = &mut ctx.accounts.base_account;
        let user = &mut ctx.accounts.user;
        let index_as_usize = index as usize;

        base_account.post_list[index_as_usize].points -= 1;
        Ok(())
    }   
//This is at the top of App.js
window.Buffer = buffer.Buffer;

Thanks again!

5
  • I can see the instructions assume BaseAccount already exists. Where are you initialising this account and giving it a certain length?
    – sohrab
    Jul 26, 2022 at 2:59
  • I've added the function that initializes BaseAccount to my question. Thanks for your help! Jul 26, 2022 at 3:08
  • I more meant the account itself. Before it can be used, it has to be initialised with a specific space (and pay the rent). This can be done either off-chain or on-chain (anchor-lang.com/docs/space). I'm just wondering how you are doing that. I maybe missing something here...
    – sohrab
    Jul 26, 2022 at 3:12
  • @sohrab okay i think I know what you mean I added this as well right now Jul 26, 2022 at 3:24
  • I added it after the RPC Jul 26, 2022 at 3:25

2 Answers 2

1

It sounds like you might have forgotten to update the IDL file used on your client, after upgrading you program structs. Verify that the structs in the IDL used client-side match the latest structs program-side.

3
  • Thanks for the answer! I updated idl.json in my frontend from the file under target/idl in my Anchor project right after I redeployed so it should be updated but I'm not sure if I missed a step. I did this before but maybe I messed something up because I added a new property to my custom struct? Jul 26, 2022 at 3:14
  • If you updated it should be unrelated then :0 It's just a classic so I wanted to have it up here, in case and for future reference/troubleshooting.
    – man0s
    Jul 26, 2022 at 4:24
  • Thanks I bet it'll help a lot of people that are also stuck on this problem Jul 26, 2022 at 12:21
1

I feel you are running out of space on base_account.

You have allocated 10000 bytes. Let's look at how much space it will take to store this:

8 [discriminator] + 8 [total_posts] + 4 [vector prefix] +
(
    (4 + ???) [post_title] +
    (4 + ???) [post_intro] +
    (4 + ???) [post_body] +
    (4 + ???) [post_conclusion] +
    32 [user_address] +
    8 [points]
) * number_of_posts

You can increase the size of the account but I think you need to re-think your account design if hoping for this to scale at all.

My initial suggestion would be to create an account per post. But if we are thinking realistically and given the size limits on Solana accounts, you may want to store the post content off-chain (or in a more suitable chain like arweave) and only track users and points on-chain.

(Sorry if this doesn't answer your question directly.)

5
  • Thanks for the advice sohrab it's very helpful. I think I'm going to refactor this project to be more off chain like you said but for now if I just want to increase the space on my program account how would I do that? I've tried to change 10,000 to something like 15,000 and it gives me an error when testing. Jul 26, 2022 at 12:20
  • While initializing the account through the macro, the maximum size is 10kb since it makes a cpi call system program to initialize it. Since it needs to know beforehand, how much space the account might take, having more size like 10mb would take more ram and more energy. So if you are trying to initialize the account using the macro then the limit is only 10kb . But if you really want to initialize account with more than 10kb, you can do without using the macro by calling the system program on the client side and then passing the account. You can define space of 10Mb maximum. Jul 26, 2022 at 13:08
  • @sohrab I saw one of your other posts so could I reallocate space like this: base_account.realloc(255, false)? Or should I put a larger number/set the boolean to true instead? Where would I call this? Jul 26, 2022 at 13:24
  • @DhruvDJain can you give me an example of how I would call system program? I would call it from my frontend? Jul 26, 2022 at 13:28
  • i think you can refer this, it has complete explanation on how you can achieve it. solana.stackexchange.com/questions/43/… Jul 26, 2022 at 14:36

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.