瀏覽代碼

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

acheron 1 年之前
父節點
當前提交
257b560109
共有 4 個文件被更改,包括 41 次插入15 次删除
  1. 1 0
      CHANGELOG.md
  2. 9 2
      lang/src/lib.rs
  3. 25 4
      tests/misc/programs/lamports/src/lib.rs
  4. 6 9
      tests/misc/tests/lamports/lamports.ts

+ 1 - 0
CHANGELOG.md

@@ -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)).
 - 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: Return overflow error from `Lamports` trait operations ([#2907](https://github.com/coral-xyz/anchor/pull/2907)).
 
 ### Fixes
 

+ 9 - 2
lang/src/lib.rs

@@ -28,6 +28,7 @@ extern crate self as anchor_lang;
 use bytemuck::{Pod, Zeroable};
 use solana_program::account_info::AccountInfo;
 use solana_program::instruction::AccountMeta;
+use solana_program::program_error::ProgramError;
 use solana_program::pubkey::Pubkey;
 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.
     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)
     }
 
@@ -215,7 +219,10 @@ pub trait Lamports<'info>: AsRef<AccountInfo<'info>> {
     ///
     /// See [`Lamports::add_lamports`] for adding lamports.
     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)
     }
 }

+ 25 - 4
tests/misc/programs/lamports/src/lib.rs

@@ -6,7 +6,7 @@ declare_id!("Lamports11111111111111111111111111111111111");
 pub mod lamports {
     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 signer = &ctx.accounts.signer;
 
@@ -52,13 +52,29 @@ pub mod lamports {
 
         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)]
-pub struct TestLamportsTrait<'info> {
+pub struct Transfer<'info> {
     #[account(mut)]
     pub signer: Signer<'info>,
-
     #[account(
         init,
         payer = signer,
@@ -67,9 +83,14 @@ pub struct TestLamportsTrait<'info> {
         bump
     )]
     pub pda: Account<'info, LamportsPda>,
-
     pub system_program: Program<'info, System>,
 }
 
+#[derive(Accounts)]
+pub struct Overflow<'info> {
+    #[account(seeds = [b"lamports"], bump)]
+    pub pda: Account<'info, LamportsPda>,
+}
+
 #[account]
 pub struct LamportsPda {}

+ 6 - 9
tests/misc/tests/lamports/lamports.ts

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