Skip to content

Conversation

@zubayr1
Copy link
Contributor

@zubayr1 zubayr1 commented Jul 17, 2025

Instead of passing lamports as part of arguments, passing Sysvar rent account.

So forcing user to pass sysvar_rent_account instead of doing:
lamports: Rent::get()?.minimum_balance(<STATE>::LEN)

where Rent::get() makes a sysvar call directly from runtime, which makes it consume more CUs.

Besides, by internally computing lamports, the code doesn't allow devs to use excess or less lamports

Copy link
Contributor

@sonicfromnewyoke sonicfromnewyoke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice functionality, gives more safety
what do you think about one/two extra calls instead of the full copy/pasted file like so?

#[inline(always)]
pub fn invoke_signed_checked(&self, rent_account_info: &AccountInfo, signers: &[Signer]) -> ProgramResult {
    // getting lamports from rent
    let rent = Rent::from_account_info(rent_account_info?;
    let lamports = rent.minimum_balance(self.space as usize);

    // checking if the funding account has enough lamports
    if self.from.lamports() < lamports {
        return Err(ProgramError::InsufficientFunds);
    }

     self.invoke_signed(signers)
}

@zubayr1 zubayr1 marked this pull request as draft July 17, 2025 20:46
@zubayr1 zubayr1 force-pushed the main branch 7 times, most recently from 074af97 to 2195ba5 Compare July 17, 2025 21:24
@zubayr1
Copy link
Contributor Author

zubayr1 commented Jul 17, 2025

nice functionality, gives more safety what do you think about one/two extra calls instead of the full copy/pasted file like so?

#[inline(always)]
pub fn invoke_signed_checked(&self, rent_account_info: &AccountInfo, signers: &[Signer]) -> ProgramResult {
    // getting lamports from rent
    let rent = Rent::from_account_info(rent_account_info?;
    let lamports = rent.minimum_balance(self.space as usize);

    // checking if the funding account has enough lamports
    if self.from.lamports() < lamports {
        return Err(ProgramError::InsufficientFunds);
    }

     self.invoke_signed(signers)
}

Since we are making the invoke_signed_check a public function, there is no point making the rent_account_info part of the arguments (since its already part of the struct). Maybe e4e9837 is something you are looking for?

@zubayr1 zubayr1 marked this pull request as ready for review July 17, 2025 21:30
@sonicfromnewyoke
Copy link
Contributor

nice functionality, gives more safety what do you think about one/two extra calls instead of the full copy/pasted file like so?

#[inline(always)]
pub fn invoke_signed_checked(&self, rent_account_info: &AccountInfo, signers: &[Signer]) -> ProgramResult {
    // getting lamports from rent
    let rent = Rent::from_account_info(rent_account_info?;
    let lamports = rent.minimum_balance(self.space as usize);

    // checking if the funding account has enough lamports
    if self.from.lamports() < lamports {
        return Err(ProgramError::InsufficientFunds);
    }

     self.invoke_signed(signers)
}

Since we are making the invoke_signed_check a public function, there is no point making the rent_account_info part of the arguments (since its already part of the struct). Maybe e4e9837 is something you are looking for?

my point was in not copy/pasting the whole create_account.rs file just for adding one field in the struct to perform the check.

this is more DRY to add the wrapper for fn invoke_signed(), isn't it?

@febo
Copy link
Collaborator

febo commented Jul 17, 2025

Have you consider adding a "constructor" method instead? This would minimize the code duplication.

pub fn with_rent_account(
        from: &AccountInfo,
        to: &AccountInfo,
        rent: &AccountInfo,
        space: u64,
        owner: &Pubkey,
    ) -> Result<Self, ProgramError>;

@zubayr1
Copy link
Contributor Author

zubayr1 commented Jul 18, 2025

Have you consider adding a "constructor" method instead? This would minimize the code duplication.

pub fn with_rent_account(
        from: &AccountInfo,
        to: &AccountInfo,
        rent: &AccountInfo,
        space: u64,
        owner: &Pubkey,
    ) -> Result<Self, ProgramError>;

Please have a look at c709b11

Comment on lines 44 to 50
if from.lamports() < lamports {
return Err(ProgramError::InsufficientFunds);
}

if !to.data_is_empty() {
return Err(ProgramError::InvalidAccountData);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too sure if we need this checks – the CPI will do this validation and raised the appropriate error.

Comment on lines 58 to 68
if seed.len() > MAX_SEED_LEN {
return Err(ProgramError::InvalidInstructionData);
}

if from.lamports() < lamports {
return Err(ProgramError::InsufficientFunds);
}

if !to.data_is_empty() {
return Err(ProgramError::InvalidAccountData);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, might be better to remove this checks.

Copy link
Collaborator

@febo febo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer this version than the previous. One point is whether we need the validation or not, since the CPI will do that. Can you measure the difference is CUs? Ideally we would like the "original" version, rent check (no validation), rent check + validation.

@zubayr1
Copy link
Contributor Author

zubayr1 commented Jul 20, 2025

I prefer this version than the previous. One point is whether we need the validation or not, since the CPI will do that. Can you measure the difference is CUs? Ideally we would like the "original" version, rent check (no validation), rent check + validation.

Please have a look at: 68cf5be

I checked with the pinocchio starter code https://github.com/Nagaprasadvr/solana-pinocchio-starter (initialize_state.rs but with only codes relevant to the test).
Keeping everything else same except only the necessary codes for the different scenarios:

CreateAccount::with_rent_check (forcing rent to be calculated efficiently internally in CreateAccount): 3073 of 1400000 compute units
CreateAccount (with lamports and with rent = Rent::from_account_info(sysvar_rent_acc)?): 3075 of 1400000 compute units
CreateAccount (with lamports but with rent = Rent::get()?) : 3177 of 1400000 compute units

Copy link
Collaborator

@febo febo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final nit.

Copy link
Collaborator

@febo febo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

@febo febo merged commit 0f3cc2f into anza-xyz:main Aug 14, 2025
11 checks passed
Nagaprasadvr pushed a commit to Nagaprasadvr/pinocchio that referenced this pull request Aug 14, 2025
…yz#204)

* use Sysvar rent account

* implement invoke_signed_checked

* removed the checked files

* implement with_rent_check constructor

* fix imports

* remove checks

* Apply suggestions from code review

Co-authored-by: Fernando Otero <[email protected]>

* fix formatting

* Apply suggestions from code review

Co-authored-by: Fernando Otero <[email protected]>

---------

Co-authored-by: Fernando Otero <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants