lang: Return overflow error from `Lamports` trait operations (#2907)

This commit is contained in:
acheron 2024-04-14 23:04:51 +02:00 committed by GitHub
parent 95c4959287
commit 257b560109
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 15 deletions

View File

@ -44,6 +44,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- idl: Store deployment addresses for other clusters ([#2892](https://github.com/coral-xyz/anchor/pull/2892)). - idl: Store deployment addresses for other clusters ([#2892](https://github.com/coral-xyz/anchor/pull/2892)).
- lang: Add `Event` utility type to get events from bytes ([#2897](https://github.com/coral-xyz/anchor/pull/2897)). - lang: Add `Event` utility type to get events from bytes ([#2897](https://github.com/coral-xyz/anchor/pull/2897)).
- lang, spl: Add support for [token extensions](https://solana.com/solutions/token-extensions) ([#2789](https://github.com/coral-xyz/anchor/pull/2789)). - lang, spl: Add support for [token extensions](https://solana.com/solutions/token-extensions) ([#2789](https://github.com/coral-xyz/anchor/pull/2789)).
- lang: Return overflow error from `Lamports` trait operations ([#2907](https://github.com/coral-xyz/anchor/pull/2907)).
### Fixes ### Fixes

View File

@ -28,6 +28,7 @@ extern crate self as anchor_lang;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use solana_program::account_info::AccountInfo; use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta; use solana_program::instruction::AccountMeta;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey; use solana_program::pubkey::Pubkey;
use std::{collections::BTreeSet, fmt::Debug, io::Write}; use std::{collections::BTreeSet, fmt::Debug, io::Write};
@ -197,7 +198,10 @@ pub trait Lamports<'info>: AsRef<AccountInfo<'info>> {
/// ///
/// See [`Lamports::sub_lamports`] for subtracting lamports. /// See [`Lamports::sub_lamports`] for subtracting lamports.
fn add_lamports(&self, amount: u64) -> Result<&Self> { fn add_lamports(&self, amount: u64) -> Result<&Self> {
**self.as_ref().try_borrow_mut_lamports()? += amount; **self.as_ref().try_borrow_mut_lamports()? = self
.get_lamports()
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(self) Ok(self)
} }
@ -215,7 +219,10 @@ pub trait Lamports<'info>: AsRef<AccountInfo<'info>> {
/// ///
/// See [`Lamports::add_lamports`] for adding lamports. /// See [`Lamports::add_lamports`] for adding lamports.
fn sub_lamports(&self, amount: u64) -> Result<&Self> { fn sub_lamports(&self, amount: u64) -> Result<&Self> {
**self.as_ref().try_borrow_mut_lamports()? -= amount; **self.as_ref().try_borrow_mut_lamports()? = self
.get_lamports()
.checked_sub(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(self) Ok(self)
} }
} }

View File

@ -6,7 +6,7 @@ declare_id!("Lamports11111111111111111111111111111111111");
pub mod lamports { pub mod lamports {
use super::*; use super::*;
pub fn test_lamports_trait(ctx: Context<TestLamportsTrait>, amount: u64) -> Result<()> { pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
let pda = &ctx.accounts.pda; let pda = &ctx.accounts.pda;
let signer = &ctx.accounts.signer; let signer = &ctx.accounts.signer;
@ -52,13 +52,29 @@ pub mod lamports {
Ok(()) Ok(())
} }
// Return overflow error in the case of overflow (instead of panicking)
pub fn overflow(ctx: Context<Overflow>) -> Result<()> {
let pda = &ctx.accounts.pda;
match pda.add_lamports(u64::MAX) {
Err(e) => assert_eq!(e, ProgramError::ArithmeticOverflow.into()),
_ => unreachable!(),
}
match pda.sub_lamports(u64::MAX) {
Err(e) => assert_eq!(e, ProgramError::ArithmeticOverflow.into()),
_ => unreachable!(),
}
Ok(())
}
} }
#[derive(Accounts)] #[derive(Accounts)]
pub struct TestLamportsTrait<'info> { pub struct Transfer<'info> {
#[account(mut)] #[account(mut)]
pub signer: Signer<'info>, pub signer: Signer<'info>,
#[account( #[account(
init, init,
payer = signer, payer = signer,
@ -67,9 +83,14 @@ pub struct TestLamportsTrait<'info> {
bump bump
)] )]
pub pda: Account<'info, LamportsPda>, pub pda: Account<'info, LamportsPda>,
pub system_program: Program<'info, System>, pub system_program: Program<'info, System>,
} }
#[derive(Accounts)]
pub struct Overflow<'info> {
#[account(seeds = [b"lamports"], bump)]
pub pda: Account<'info, LamportsPda>,
}
#[account] #[account]
pub struct LamportsPda {} pub struct LamportsPda {}

View File

@ -8,16 +8,13 @@ describe("lamports", () => {
const program = anchor.workspace.Lamports as anchor.Program<Lamports>; const program = anchor.workspace.Lamports as anchor.Program<Lamports>;
it("Can use the Lamports trait", async () => { it("Can transfer from/to PDA", async () => {
const signer = program.provider.publicKey!;
const [pda] = anchor.web3.PublicKey.findProgramAddressSync(
[Buffer.from("lamports")],
program.programId
);
await program.methods await program.methods
.testLamportsTrait(new anchor.BN(anchor.web3.LAMPORTS_PER_SOL)) .transfer(new anchor.BN(anchor.web3.LAMPORTS_PER_SOL))
.accounts({ signer, pda })
.rpc(); .rpc();
}); });
it("Returns an error on overflow", async () => {
await program.methods.overflow().rpc();
});
}); });