1

I have a PDA account, and I am trying to change the data format in the account.

from this

#[account]
pub dummy_data {
  x_point: f32
} 

to

#[account]
pub dummy_data {
  x_point: u64,
  y_point: f64,
  point_name: String,
} 

I am getting this error when trying to do so.

errorLogs: [
    'Program log: AnchorError caused by account: dummy_data. Error Code: AccountDidNotDeserialize. Error Number: 3003. Error Message: Failed to deserialize the account.'
  ],

2 Answers 2

5

Yes you can but you need to work around anchor to do this.

First thing is, as Ademola said in another answer, you need to make sure you adapt the size of the account to fit your new data so do that before continuing.

Second, we need to understand how anchor treats accounts and why you're getting "Failed to deserialize the account" error.

Solana accounts are just bytes, and you can modify them however you want, Solana blockchain will not complain. But in practice you want those bytes to represent some Rust data that you can interact with, usually a struct like your dummy_data. So in Solana programs the first thing that happens is you take those bytes that are in your account and then deserialize them into Rust structs (dummy_data for example).

Now Anchor does this automatically for you. When you tell Anchor what accounts your instruction expects, for example this code:

#[derive(Accounts)]
pub struct MyIxAccounts<'info> {
    pub dummy_data_account: Account<'info, dummy_data>,
    #[account(mut)]
    pub signer: Signer<'info>
}

If will automatically convert/deserialize the bytes that are in the dummy_data_account account into a dummy_data struct which you will then use in your instruction to process.

What happens in your case when you get the "Failed to deserialize the account" error is that anchor is taking bytes from an account that was populated with the old version of the dummy_data struct and trying to convert those bytes into the new dummy_data struct and it cannot because they simply don't match. Like trying to use puzzle pieces to create a different image than the original puzzle was made from.

If you want to change the structure of the dummy_data account what you need to to is to not have anchor deserialise it automatically. So instead of this:

#[derive(Accounts)]
pub struct MigrateDummyData<'info> {
    pub dummy_data_account: Account<'info, dummy_data>,
  ...
}

You'd use this:

#[derive(Accounts)]
pub struct MigrateDummyData<'info> {
    pub dummy_data_account: AccountInfo<'info>,
  ...
}

Because Account will deserialize data (which fails since we changed the dummy_data struct), so we replace it with AccountInfo which does not deserialize data automatically.

Now the following instruction should convert your old account into the new dummy_data account:

pub fn migrate_dummy_data(ctx: Context<MigrateDummyData>, new_data) -> Result<()> {
        // create our new data that we want to put into our old account
        let new_data = dummy_data {
            x_point: new_data.x, 
            y_point: new_data.y,
            point_name: new_data.name,
        };
        let mut new_data_vec = new_data.try_to_vec().unwrap();

        // get the existing old-format data from the existing account 
        // and make it mutable (we're just going to replace it)
        let dummy_data_account = &mut ctx.accounts.dummy_data_account;
        let mut dummy_data_data = dummy_data_account.try_borrow_mut_data()?;
        
        // Copy the bytes from the new data struct into the old account, 
        // but skip the first 8 bytes because those are the account 
        // discriminator used by anchor, basically a sort of 'id' anchor 
        // uses to know what kind of data this is, in this case it's 
        // `dummy_data` and that does not change so we leave the first 
        // 8 bytes unchanged.
        dummy_data_data[8..new_data_vec.len()].copy_from_slice(&new_data_vec[8..new_data_vec.len()]);
        
        Ok(())
    }

After the above instruction is executed, you should be able to use the account as you normally would (using Account<'info, dummy_data> ) and you shouldn't get "Failed to deserialize the account" error anymore.

This is obviously pretty complex and it should be thoroughly tested before doing it in production.

Note: When changing the account structs, do not rename them (dummy_data to dummy_data_new for example) because the name is actually used in the way anchor/rust structures the bytes and renaming the struct will make deserialization not work after renaming.

3

Unless you increase the space allocation for your account, it can only store a maximum of the number of bytes it was allocated during initialization(in this case likely 8 + 4). Hence deserialization fails because the account doesn't hold enough bytes to deconstruct the new type from.

To make serialization and deserialization work fine again, increase the data size of the account using realloc.

Here's how to reallocate an account's size in anchor.

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.