2

Is there a way to create and initialize an account in Anchor without a discriminator?

I tried testing with an initialize instruction that makes a CPI to system program to create an account and then deserialize/serialize the data for new account. It seems to work, but wondering if there's a simpler way.

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
    let space = 32;
    let lamports = Rent::get()?.minimum_balance(space as usize);

    let ix = anchor_lang::solana_program::system_instruction::create_account(
        &ctx.accounts.payer.key(),
        &ctx.accounts.user.key(),
        lamports,
        space,
        &ctx.program_id,
    );

    anchor_lang::solana_program::program::invoke(
        &ix,
        &[
            ctx.accounts.payer.to_account_info(),
            ctx.accounts.user.to_account_info(),
            ctx.accounts.system_program.to_account_info(),
        ],
    )?;

    let mut account = User::try_from_slice(&ctx.accounts.user.data.borrow()).unwrap();

    account.user = ctx.accounts.payer.key();
    account.serialize(&mut *ctx.accounts.user.data.borrow_mut())?;

    msg!("Public Key: {}", account.user.to_string());
    Ok(())
}

3 Answers 3

1

That is rougly how you initialize accounts with Native and similar to what Anchor's init macro does under the hood. I don't think there is a simpler way, but also note that you will not be able to deserialize this account from the client side by default because Anchor always assumes you have the 8 byte discriminator up-front.

1

Why you need to skip the Anchor discriminator? If you want to use the same account struct in different anchor programs you can do something like:

let account_data = ctx.accounts.rate_program_state.try_borrow_data()?;
let mut account_data_slice: &[u8] = &account_data;
let rate_state = RateState::try_deserialize_unchecked(&mut account_data_slice)?;

The 8byte discriminator is still there, created during initialization, but you can use the account struct without checking it.

0

I have some code that looks very similar to this but its not working. What I'm trying to do is do a CPI "invoke_signed" and have the &[accounts] parameter of "invoke_signed" be the accounts that i get from Anchor's Context.

pub fn create_ad(
        ctx: Context<CreateAdAccount>,
        ad_name: String,
        link: String,
        time: String,
        money: String,
        approved_providers: Vec<Pubkey>,
        status: String,
    ) -> Result<()> {
        msg!("Adding Ad...");
        msg!("Account Name: {}", ad_name);

        let new_ad = &mut ctx.accounts.ad;
        new_ad.discriminator = "ad".to_string();
        new_ad.is_initialized = true;
        new_ad.name = ad_name;
        new_ad.link = link;
        new_ad.time = time;
        new_ad.money = money;
        new_ad.approved_providers = approved_providers.clone();
        new_ad.status = status;

        let program_id = Pubkey::from_str("HB8LMbzurJuJvdEXNkTb1oetqohVb4PRPdbGGQCUGSY5").unwrap();

        for (index, provider_pubkey) in approved_providers.iter().enumerate() {
            let provider_ad_relation_account_info = &ctx.remaining_accounts[index];
            let new_ad_pubkey = new_ad.key();

            let seeds = &[
                "relation".as_ref(),
                provider_pubkey.as_ref(),
                new_ad_pubkey.as_ref(),
            ];

            let (provider_ad_pda, bump_seed) = Pubkey::find_program_address(seeds, &program_id);

            if provider_ad_pda != *provider_ad_relation_account_info.key {
                msg!("Invalid PDA for provider");
                return Ok(());
            }

            let account_len = 100;
            let rent = Rent::get()?;
            let rent_lamports = rent.minimum_balance(account_len);

            invoke_signed(
                &system_instruction::create_account(
                    ctx.accounts.signer.key,
                    provider_ad_relation_account_info.key,
                    rent_lamports,
                    account_len.try_into().unwrap(),
                    &program_id,
                ),
                &[
                    ctx.accounts.signer.to_account_info(),
                    provider_ad_relation_account_info.clone(),
                    ctx.accounts.signer.to_account_info(),
                ],
                &[&[
                    "relation".as_ref(),
                    provider_pubkey.as_ref(),
                    new_ad_pubkey.as_ref(),
                    &[bump_seed],
                ]],
            )?;

            msg!("unpacking state account");
            let mut account_data = try_from_slice_unchecked::<ProviderAdRelationState>(
                &provider_ad_relation_account_info.data.borrow(),
            ).unwrap();
            msg!("borrowed account data");

            account_data.discriminator = "relation".to_string();
            account_data.provider = *provider_pubkey;
            account_data.ad = new_ad_pubkey;
            account_data.state = 0; // PENDING

            msg!("serializing account");
            account_data
                .serialize(&mut &mut provider_ad_relation_account_info.data.borrow_mut()[..])?;
            msg!("state account serialized");
        }

        Ok(())
    }
}

Everything looks good so far, but when i try to compile i get this.

error: lifetime may not live long enough
  --> /src/lib.rs:92:21
   |
38 |         ctx: Context<CreateAdAccount>,
   |         ---
   |         |
   |         has type `anchor_lang::context::Context<'_, '_, '_, '_, CreateAdAccount<'2>>`
   |         has type `anchor_lang::context::Context<'_, '_, '_, '1, CreateAdAccount<'_>>`
...
92 |                     ctx.accounts.signer.to_account_info(),
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'2` must outlive `'1`
   |
   = note: requirement occurs because of the type `anchor_lang::prelude::Signer<'_>`, which makes the generic argument `'_` invariant
   = note: the struct `anchor_lang::prelude::Signer<'info>` is invariant over the parameter `'info`
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

Im not super familiar with lifetimes and Rust in general. I would like to know what i can do to get around getting this error. And also what is causing it in the first place.

Thank you

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.