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.