2

I am trying to build a kind of a complex instruction in which there are lots of accounts and variables. I guess, I can't handle memory well that why there are some errors like "Access violation in unknown section error"

I am also using Box<> wrapper for smart pointers to reduce stack usage.

How can I solve that issue? I am not sure if I use zero_copy account constraint

#[derive(Accounts)]
#[instruction(data: FractionSplitData)]
pub struct FractionSplit<'info> {
    #[account(
        mut @ Error::AccountNotMutable,
        signer @ Error::AccountNotSigner
    )]
    pub payer: Signer<'info>,

    #[account(
        seeds = [ADMINISTRATOR_SEED],
        seeds::program = administrator::id(),
        bump = administrator.bump
    )]
    pub administrator: Box<Account<'info, Administrator>>,
    // TODO: fraction_split_authority is required or not! Be sure that. If the pda that has fraction_create_authority also can split fractions, no problem
    #[account(
        signer @ Error::AccountNotSigner,
    )]
    pub user: Signer<'info>,
   
    /// CHECK: checked in MPL Core
    pub owner: AccountInfo<'info>,
    #[account(
        address = base_fraction.application
    )]
    pub application: Box<Account<'info, Application>>,
    /// CHECK: Checked in fraction_create
    #[account(
        seeds = [
            IDENTITY_SEED,
            application.key().as_ref(),
            identity.id.as_ref()
        ],
        seeds::program = guardian::id(),
        bump = identity.bump,
    )]
    pub identity: Box<Account<'info, Identity>>,
    #[account(
        address = base_fraction.asset
    )]
    pub asset: Box<Account<'info, Asset>>,

    /// CHECK: Checked in mpl-core.
    #[account(
        mut @ Error::AccountNotMutable,
        seeds = [
            ASSET_MPL_CORE_COLLECTION_SEED,
            application.key().as_ref(),
            asset.key().as_ref()
        ],
        bump = Asset::asset_mpl_core_collection(application.key(), asset.key()).1,
        constraint = asset_mpl_core_collection.key() == asset.asset_mpl_core_collection.unwrap().clone().key()
            @ Error::AccountAssetMplCoreCollectionInvalid
    )]
    pub asset_mpl_core_collection: AccountInfo<'info>,
    #[account(
        mut,
        seeds = [
            FRACTION_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            data.base_fraction_id.as_ref()
        ],
        bump = base_fraction.bump
    )]
    pub base_fraction: Box<Account<'info, Fraction>>,

    /// CHECK: Checked in mpl-core.
    #[account(
        mut @ Error::AccountNotMutable,
        seeds = [
            FRACTION_MPL_CORE_ASSET_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            base_fraction.key().as_ref()
        ],
        bump = Fraction::fraction_mpl_core_asset(application.key(), asset.key(), base_fraction.key()).1
    )]
    pub base_fraction_mpl_core_asset: AccountInfo<'info>,
    #[account(
        init,
        payer = payer,
        space = 8 + Fraction::INIT_SPACE,
        seeds = [
            FRACTION_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            data.new_first_fraction_id.as_ref()
        ],
        bump
    )]
    pub new_first_fraction: Box<Account<'info, Fraction>>,
    #[account(
        init,
        payer = payer,
        space = 8 + Fraction::INIT_SPACE,
        seeds = [
            FRACTION_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            data.new_second_fraction_id.as_ref()
        ],
        bump
    )]
    pub new_second_fraction: Box<Account<'info, Fraction>>,

    /// CHECK: Checked in mpl-core.
    #[account(
        mut @ Error::AccountNotMutable,
        seeds = [
            FRACTION_MPL_CORE_ASSET_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            new_first_fraction.key().as_ref()
        ],
        bump = Fraction::fraction_mpl_core_asset(application.key(), asset.key(), new_first_fraction.key()).1
    )]
    pub new_first_fraction_mpl_core_asset: AccountInfo<'info>,
    /// CHECK: Checked in mpl-core.
    #[account(
        mut @ Error::AccountNotMutable,
        seeds = [
            FRACTION_MPL_CORE_ASSET_SEED,
            application.key().as_ref(),
            asset.key().as_ref(),
            new_second_fraction.key().as_ref()
        ],
        bump = Fraction::fraction_mpl_core_asset(application.key(), asset.key(), new_second_fraction.key()).1
    )]
    pub new_second_fraction_mpl_core_asset: AccountInfo<'info>,

    pub system_program: Program<'info, System>,

    /// CHECK: The MPL Core program
    #[account(address = mpl_core::ID)]
    pub mpl_core_program: AccountInfo<'info>,
}

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct FractionSplitData {
    pub base_fraction_id: Pubkey,
    pub new_first_fraction_id: Pubkey,
    pub new_first_fraction_name: String,
    pub new_first_fraction_uri: String,
    pub new_second_fraction_id: Pubkey,
    pub new_second_fraction_name: String,
    pub new_second_fraction_uri: String,
    pub split_ratios: Vec<u64>,
}

//impl method
// TODO: add validations and handling errors
impl<'info> FractionSplit<'info> {
    pub fn process(&mut self, data: FractionSplitData) -> Result<()> {

        let base_fraction_key = self.base_fraction.key();

        let first_fraction_key = self.new_first_fraction.key();
        let first_fraction_bump = self.new_first_fraction.bump;

        let second_fraction_key = self.new_second_fraction.key();
        let second_fraction_bump = self.new_second_fraction.bump;


        
       

        //determine the split ratios. Think the originial token amount is 10, and user wants to split it 2:3
        // Validate split ratios first to fail early
        let first_ratio = data.split_ratios.get(0).ok_or(Error::InvalidSplitRatio)?;
        let second_ratio = data.split_ratios.get(1).ok_or(Error::InvalidSplitRatio)?;
        
        let total_ratio = first_ratio
            .checked_add(*second_ratio)
            .ok_or(Error::FractionSplitRatioOverflow)?;

        require!(total_ratio > 0, Error::InvalidSplitRatio);

        
        // Calculate new amounts once
            let base_amount = self.base_fraction.tokens_amount;
            let first_amount = base_amount
                .checked_mul(*first_ratio)
                .and_then(|v| v.checked_div(total_ratio))
                .ok_or(Error::FractionSplitRatioOverflow)?;
                
            let second_amount = base_amount
                .checked_mul(*second_ratio)
                .and_then(|v| v.checked_div(total_ratio))
                .ok_or(Error::FractionSplitRatioOverflow)?;

        msg!("new_fraction_amounts are calculated");

        // ---------  MARK: create the new fractions ---------

        self.new_first_fraction.create(
            FractionCreateData {
                fraction_id: data.new_first_fraction_id,
                fraction_name: data.new_first_fraction_name,
                fraction_uri: data.new_first_fraction_uri,
                tokens_amount: first_amount,
            },
            &self.identity,
            &self.new_first_fraction_mpl_core_asset,
            &self.payer,
            &self.owner,
            &self.system_program,
            &self.mpl_core_program,
            &self.application,
            &self.asset,
            &self.asset_mpl_core_collection,
            first_fraction_key,
            first_fraction_bump,
        )?;
        msg!("new_first_fraction created");
        
        self.new_second_fraction.create(
            FractionCreateData {
                fraction_id: data.new_second_fraction_id,
                fraction_name: data.new_second_fraction_name,
                fraction_uri: data.new_second_fraction_uri,
                tokens_amount: second_amount,
            },
            &self.identity,
            &self.new_second_fraction_mpl_core_asset,
            &self.payer,
            &self.owner,
            &self.system_program,
            &self.mpl_core_program,
            &self.application,
            &self.asset,
            &self.asset_mpl_core_collection,
            second_fraction_key,
            second_fraction_bump,
        )?;
        msg!("new_second_fraction created");
        // burn the original fraction

        self.base_fraction.burn(
            &self.identity,
            &self.base_fraction_mpl_core_asset,
            &self.payer,
            &self.mpl_core_program,
            &self.application,
            &self.asset,
            &self.asset_mpl_core_collection,
            base_fraction_key,
        )?;

        

        // close base_fraction account
        self.base_fraction.close(self.payer.to_account_info())?;
       

        Ok(())
    }
}


1 Answer 1

1

When you compile your program you should already see a warning if you run out of memory space. That should give you a good hint which accounts are overflowing space.

Also make sure to upgrade anchor and solana version to the newest version, there was some stack space issues a few months back. Especially in 1.18.15.

Boxing is already nice. If that doesnt help you can split up transactions or use zero copy like you already said.

Here are some zero copy examples: https://github.com/solana-developers/anchor-zero-copy-example

And here a game that uses a very big game board struct as an examples implementation: https://github.com/solana-developers/solana-game-examples/blob/main/seven-seas/program/programs/seven-seas/src/state/game.rs

The examples are up to date with newest anchor version. For the game im not 100%, but it shows the concept.

1
  • 1
    I think, updating the solana and anchor to the newest version result in backward compatibility issues as the program that I've been building a part of the instruction is on production. Thanks for your advice, I will try others Commented Nov 19 at 8:10

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.